目录
题目
思路和解题方法
问题分析
本题为模拟题,初看下来情况较多,需要逐步拆解来简化问题。
首先,问题的复杂性主要在于月份,不同月份的天数不一样;而每天的时分秒数量都是一致的,也就是说每天触发的次数是固定的。
那么我们可以考虑两步走:先用时、分、秒计算出每天触发的次数;再计算一年总共会触发的天数,二者相乘即为答案。
这里还顺带解决了另一个小问题:时分秒是从 开始计数,而月日是从 开始的;我们分开计算就不用额外考虑这里的细节了。
除此之外,本题还需要解决一个字符串解析的问题,要从单个域的表达式中提取出它所表达的数字,对此需要进行封装。
本题主要难点在于如何实现,下面给出一种简单清晰的写法。
实现细节
附上我另一个以前写的日期问题(模板): 2017省赛---日期问题(暴力 枚举)https://blog.csdn.net/jgk666666/article/details/130008471?
先手写一下每个月份对应的天数存在数组里, 年为平年:
int mdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
封装这样一个函数 count ,作用是解析单个域的表达式,返回一个 vector 包含该域所有可能的结果:
vector<int> count(const string &s, int tot) { vector<int> result; if (s == "*") { for (int i = 1; i <= tot; ++i) { result.push_back(i); } return result; } int lst = -1, cur = 0; for (char c : s) { if (c == ',') { if (cur <= tot) { result.push_back(cur); } cur = 0; } else if (c == '-') { lst = cur; cur = 0; } else { cur = cur * 10 + c - '0'; } } if (lst != -1) { for (int i = lst; i <= min(cur, tot); ++i) { result.push_back(i); } } else if (cur <= tot) { result.push_back(cur); } return result; }
字符串解析部分:我们逐个读入字符并维护当前数字 ,遇到普通字符 时,就按照 进行维护;遇到 , 时记录当前数字;遇到 - 时则需要将 记录下来,解析完下一个数字时,再获得整段区间的结果。
由于本题保证了每个域内只出现一种特殊字符且合法,我们可以少考虑很多情况。
该写法的关键在于这个函数的第二个参数, 表示该表达式的取值上限,它有两个功能:一个是在表达式为 * 时能返回正确的数量,另一个就是限制取值上限,在函数内,我们判断了这一点,不会选取超过 的数字。
有了这个功能,在不同月份,我们就可以使用不同的 值来方便地计算当月的合法天数。
有了这样一个函数后,主体计算部分就非常简单了:首先将时、分、秒各自的次数相乘,得到每天的触发次数;然后再获取所有触发的月份,暴力枚举,单独计算每个月的触发天数。string S, M, H, D, Mon; cin >> S >> M >> H >> D >> Mon; ll pday = count(S, 60).size() * count(M, 60).size() * count(H, 24).size(); ll ans = 0; for(auto m: count(Mon, 12)) { ans += count(D, mdays[m]).size() * pday; }
复杂度
时间复杂度:O(m * k)
时间复杂度可以认为是O(m * k)
在最坏的情况下,这可能接近O(12 * k_max),其中k_max是所有日期或时间字段解析出的最大数字序列长度。
空间复杂度:O(n)
(n),其中n是单个字段可能产生的最大数字序列长度。考虑到实际应用中通常不会有极端的cron表达式,实际空间消耗会低于这个理论上的最坏情况。
c++ 代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
// 静态数组存储每个月的天数,注意索引0是无效的,用于方便计算
static int[] mdays = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/**
* 解析输入的字符串,并根据指定的总数生成有效的整数列表。
* 支持逗号分隔的数字、范围(如"1-5")以及星号(表示范围内的所有数字)。
*
* @param s 输入的字符串,可能包含数字、逗号、破折号或星号
* @param tot 总数,用于限制生成数字的最大值(如月份最大为12)
* @return 符合条件的整数列表
*/
static List<Integer> count(String s, int tot) {
List<Integer> result = new ArrayList<>();
// 如果字符串为星号,则生成从1到tot的所有整数
if (s.equals("*")) {
for (int i = 1; i <= tot; i++) {
result.add(i);
}
return result;
}
int lst = -1; // 记录范围开始的数字
int cur = 0; // 当前处理的数字
for (char c : s.toCharArray()) {
if (c == ',') {
// 遇到逗号,添加当前累积的数字到结果列表
if (cur <= tot) {
result.add(cur);
}
cur = 0; // 重置当前累积数字
} else if (c == '-') {
// 遇到破折号,记录范围开始数字
lst = cur;
cur = 0;
} else {
// 普通数字字符,累加到当前数字
cur = cur * 10 + (c - '0');
}
}
// 处理结束或最后一个数字的情况
if (lst != -1) {
// 如果有范围定义,添加范围内所有数字到结果列表
for (int i = lst; i <= Math.min(cur, tot); i++) {
result.add(i);
}
} else if (cur <= tot) {
// 否则,直接添加当前累积的数字
result.add(cur);
}
return result;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取用户输入的时间表达式各部分
String S = sc.next(); // 秒
String M = sc.next(); // 分
String H = sc.next(); // 时
String D = sc.next(); // 日
String Mon = sc.next(); // 月
// 计算每小时的组合数(秒和分钟的乘积)
int pday = count(S, 60).size() * count(M, 60).size() * count(H, 24).size();
// 初始化答案计数器
int ans = 0;
// 遍历所有可能的月份,累加符合条件的日的数量
for (int m : count(Mon, 12)) {
ans += count(D, mdays[m]).size() * pday;
}
// 输出总的有效时间点数量
System.out.println(ans);
}
}
Java 版本(仅供参考)
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
// 静态数组存储每个月的天数,注意索引0是无效的,用于方便计算
static int[] mdays = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/**
* 解析输入的字符串,并根据指定的总数生成有效的整数列表。
* 支持逗号分隔的数字、范围(如"1-5")以及星号(表示范围内的所有数字)。
*
* @param s 输入的字符串,可能包含数字、逗号、破折号或星号
* @param tot 总数,用于限制生成数字的最大值(如月份最大为12)
* @return 符合条件的整数列表
*/
static List<Integer> count(String s, int tot) {
List<Integer> result = new ArrayList<>();
// 如果字符串为星号,则生成从1到tot的所有整数
if (s.equals("*")) {
for (int i = 1; i <= tot; i++) {
result.add(i);
}
return result;
}
int lst = -1; // 记录范围开始的数字
int cur = 0; // 当前处理的数字
for (char c : s.toCharArray()) {
if (c == ',') {
// 遇到逗号,添加当前累积的数字到结果列表
if (cur <= tot) {
result.add(cur);
}
cur = 0; // 重置当前累积数字
} else if (c == '-') {
// 遇到破折号,记录范围开始数字
lst = cur;
cur = 0;
} else {
// 普通数字字符,累加到当前数字
cur = cur * 10 + (c - '0');
}
}
// 处理结束或最后一个数字的情况
if (lst != -1) {
// 如果有范围定义,添加范围内所有数字到结果列表
for (int i = lst; i <= Math.min(cur, tot); i++) {
result.add(i);
}
} else if (cur <= tot) {
// 否则,直接添加当前累积的数字
result.add(cur);
}
return result;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取用户输入的时间表达式各部分
String S = sc.next(); // 秒
String M = sc.next(); // 分
String H = sc.next(); // 时
String D = sc.next(); // 日
String Mon = sc.next(); // 月
// 计算每小时的组合数(秒和分钟的乘积)
int pday = count(S, 60).size() * count(M, 60).size() * count(H, 24).size();
// 初始化答案计数器
int ans = 0;
// 遍历所有可能的月份,累加符合条件的日的数量
for (int m : count(Mon, 12)) {
ans += count(D, mdays[m]).size() * pday;
}
// 输出总的有效时间点数量
System.out.println(ans);
}
}
Python 版本(仅供参考)
# 定义一年中每个月的天数,注意二月默认为28天
mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# count函数解析输入的时间格式串,支持*、-、, 分隔符来表示时间范围
def count(s, tot):
result = [] # 初始化结果列表
if s == "*": # 如果是星号,表示范围是1到tot
for i in range(1, tot+1):
result.append(i)
return result
lst = -1 # 记录范围的开始值,默认为-1
cur = 0 # 当前处理的数字值
for c in s: # 遍历输入的字符串
if c == ",": # 遇到逗号,添加当前值到结果并重置当前值
if cur <= tot:
result.append(cur)
cur = 0
elif c == "-": # 遇到短横线,记录开始值
lst = cur
cur = 0
else: # 遇到数字,累加到当前值
cur = cur * 10 + int(c)
if lst != -1: # 如果之前遇到过短横线,处理范围
for i in range(lst, min(cur, tot)+1):
result.append(i)
elif cur <= tot: # 没有短横线,直接添加当前值
result.append(cur)
return result
# 从用户输入读取时间格式
S, M, H, D, Mon = input().split(' ')
# 计算每个小时和分钟的可能性数量
pday = len(count(S, 60)) * len(count(M, 60)) * len(count(H, 24))
# 初始化答案
ans = 0
# 遍历月份并计算每一天的可能性
for m in count(Mon, 12):
ans += len(count(D, mdays[m])) * pday
# 输出总的有效时间数量
print(ans)
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。