当前位置:首页 > 技术 > 正文内容

C++算法实践:字符串子串分值、日期计算与日期合法性验证

访客 技术 2026年8月22日 1

本文将介绍三个C++算法问题的实现,分别是关于字符串子串分值的计算、两个日期之间的天数差值计算,以及给定数字组合的日期合法性验证和排序。每个问题都提供了详细的C++代码实现。

字符串子串分值计算

本题旨在计算一个字符串中所有子串的"分值"之和,其中一个子串的分值定义为该子串中每个字符在其自身子串中首次出现或末次出现所贡献的值。具体来说,对于字符串中的每个字符s[i],其贡献度由其在字符串中所有可能出现的位置决定:(i - prev_pos) * (next_pos - i)。这里,prev_pos是字符s[i]i之前最近一次出现的位置(如果不存在,则为-1),next_pos是字符s[i]i之后最近一次出现的位置(如果不存在,则为字符串长度)。

此计算方法实际上统计的是每个字符作为其所在子串中唯一实例的子串数量。例如,对于字符串 "aba",字符 'b' 在索引1(0-based)处:其前一个 'b' 不存在 (prev_pos = -1),后一个 'b' 不存在 (next_pos = 3)。则贡献为 (1 - (-1)) * (3 - 1) = 2 * 2 = 4。这4个子串是:"b", "ab", "ba", "aba"。

#include <iostream>
#include <string>
#include <vector>

// 使用 long long 存储结果以防止溢出
long long total_score = 0;

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    std::string input_str;
    std::cin >> input_str; // 注意输入方式,这里使用 std::cin 读取字符串

    int str_len = input_str.length();

    // 存储每个字符出现的所有索引位置
    // char_occurrences[char_code - 'a'] 存储了该字符的所有0-based索引
    std::vector<std::vector<int>> char_occurrences(26); 

    for (int i = 0; i < str_len; ++i) {
        char_occurrences[input_str[i] - 'a'].push_back(i);
    }

    // 遍历每个字符类型(a-z)
    for (int char_type_idx = 0; char_type_idx < 26; ++char_type_idx) {
        const auto& positions = char_occurrences[char_type_idx];
        if (positions.empty()) {
            continue; // 该字符未在字符串中出现
        }

        // 遍历该字符的每个出现位置
        for (int j = 0; j < positions.size(); ++j) {
            int current_char_pos = positions[j]; // 当前字符的0-based索引

            // 查找前一个相同字符的位置
            // 如果是该字符的首次出现,则前一个位置视为 -1 (字符串起始前一个位置)
            int prev_pos = (j == 0) ? -1 : positions[j - 1];

            // 查找后一个相同字符的位置
            // 如果是该字符的末次出现,则后一个位置视为 str_len (字符串结束的后一个位置)
            int next_pos = (j == positions.size() - 1) ? str_len : positions[j + 1];

            // 计算该字符在此位置的贡献度
            // (current_char_pos - prev_pos) 表示左侧(包括自身)有多少个起始点
            // (next_pos - current_char_pos) 表示右侧(包括自身)有多少个结束点
            total_score += static_cast<long long>(current_char_pos - prev_pos) * 
                           static_cast<long long>(next_pos - current_char_pos);
        }
    }

    std::cout << total_score << std::endl;

    return 0;
}

日期差值计算

此问题要求计算两个给定日期之间的天数差。核心思路是将每个日期转换为自某个基准日期(例如公元1年1月1日)以来的总天数,然后取两者差值的绝对值。需要注意的是,闰年对二月份天数和全年天数的影响。

#include <iostream>
#include <vector>
#include <cmath> // For std::abs

// 存储每个月的天数,2月份会在运行时根据是否闰年调整
// 0:占位,1-12月
const std::vector<int> MONTH_DAY_COUNTS = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

// 辅助函数:判断给定年份是否为闰年
bool is_year_leap(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 辅助函数:计算从公元1年1月1日到指定日期的总天数
long long get_accumulated_days(int date_int_format) {
    long long days_since_epoch = 0;

    int year = date_int_format / 10000;
    int month = (date_int_format / 100) % 100;
    int day = date_int_format % 100;

    // 累加完整年份的天数(从公元1年到指定年份的前一年)
    for (int y = 1; y < year; ++y) {
        days_since_epoch += 365 + (is_year_leap(y) ? 1 : 0);
    }

    // 累加指定年份的完整月份天数(从1月到指定月份的前一月)
    std::vector<int> current_year_month_days = MONTH_DAY_COUNTS; // 复制一份以便修改2月份
    current_year_month_days[2] = is_year_leap(year) ? 29 : 28; // 根据当前年份调整2月份天数

    for (int m = 1; m < month; ++m) {
        days_since_epoch += current_year_month_days[m];
    }

    // 累加指定月份的日期天数
    days_since_epoch += day;
    
    return days_since_epoch;
}

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int date_val_a, date_val_b;
    while (std::cin >> date_val_a >> date_val_b) {
        long long total_days_date_a = get_accumulated_days(date_val_a);
        long long total_days_date_b = get_accumulated_days(date_val_b);
        // 加上1是因为题目通常指包含头尾的日期区间
        std::cout << std::abs(total_days_date_b - total_days_date_a) + 1 << std::endl;
    }

    return 0;
}

日期问题:格式解析与合法性验证

此问题要求从一个特定格式的输入(例如 YY/MM/DD 但数字的实际含义未知)中解析出可能的日期,进行合法性验证,并将所有合法的、去重后的日期按升序输出。输入的三个数字 N1, N2, N3 可以代表年、月、日中的任意一个,并且两位数的年份需要根据规则(如 <=60 代表20xx年,>60 代表19xx年)扩展为四位数。

原始代码仅测试了三种特定的组合方式来构建日期。这里我们依然遵循原始代码的组合逻辑,对每种组合尝试解析为 (年, 月, 日),然后进行验证和存储。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm> // For std::sort
#include <set>       // For storing unique dates and automatic sorting
#include <cstdio>    // For sscanf for specific input format

// 存储每个月的天数,2月份会在运行时根据是否闰年调整
// 0:占位,1-12月
const std::vector<int> MONTH_LENGTHS_BASE = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

// 辅助函数:判断给定年份是否为闰年
bool is_full_year_leap(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 辅助函数:检查日期是否合法
bool check_date_validity(int year, int month, int day) {
    if (year < 1959 || year > 2060) return false; // 年份范围限制
    if (month < 1 || month > 12) return false;    // 月份范围限制

    std::vector<int> current_month_days = MONTH_LENGTHS_BASE;
    current_month_days[2] = is_full_year_leap(year) ? 29 : 28; // 根据年份调整2月份天数

    if (day < 1 || day > current_month_days[month]) return false; // 日期范围限制

    return true;
}

// 存储所有发现的合法日期,set会自动去重和排序
std::set<int> found_valid_dates;

// 尝试根据给定的年、月、日组件生成并验证日期
void evaluate_date_permutation(int year_comp, int month_comp, int day_comp) {
    // 根据规则扩展两位年份到四位
    int full_year = (year_comp <= 60) ? (2000 + year_comp) : (1900 + year_comp);

    if (check_date_validity(full_year, month_comp, day_comp)) {
        // 将日期表示为整数 YYYYMMDD
        int date_numerical_format = full_year * 10000 + month_comp * 100 + day_comp;
        found_valid_dates.insert(date_numerical_format);
    }
}

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int val1, val2, val3;
    // 使用 scanf 读取 %2d/%2d/%2d 格式,因为 std::cin 默认以空格或换行符分隔,不直接支持带分隔符的固定宽度输入。
    // 这里为了匹配原题输入方式,保留 scanf。
    scanf("%2d/%2d/%2d", &val1, &val2, &val3);

    // 尝试不同的组合,将三个数字解释为 (年, 月, 日)
    // 原始代码只尝试了这三种特定排列作为 (年, 月, 日)
    evaluate_date_permutation(val1, val2, val3); // 组合1: (val1为年, val2为月, val3为日)
    evaluate_date_permutation(val3, val1, val2); // 组合2: (val3为年, val1为月, val2为日)
    evaluate_date_permutation(val3, val2, val1); // 组合3: (val3为年, val2为月, val1为日)

    // std::set 自动保持元素排序,并去重
    for (int date_val : found_valid_dates) {
        int year = date_val / 10000;
        int month = (date_val / 100) % 100;
        int day = date_val % 100;
        printf("%04d-%02d-%02d\n", year, month, day); // 格式化输出为 YYYY-MM-DD
    }

    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。