C++算法实践:字符串子串分值、日期计算与日期合法性验证
本文将介绍三个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;
}