蓝桥杯第17115题 定时任务 C++ Java Python

目录

题目

思路和解题方法

复杂度

        时间复杂度:O(m * k)

        空间复杂度:O(n)

c++ 代码

Java 版本(仅供参考)

Python 版本(仅供参考)


题目

思路和解题方法

问题分析
本题为模拟题,初看下来情况较多,需要逐步拆解来简化问题。
首先,问题的复杂性主要在于月份,不同月份的天数不一样;而每天的时分秒数量都是一致的,也就是说每天触发的次数是固定的。
那么我们可以考虑两步走:先用时、分、秒计算出每天触发的次数;再计算一年总共会触发的天数,二者相乘即为答案。
这里还顺带解决了另一个小问题:时分秒是从 开始计数,而月日是从 开始的;我们分开计算就不用额外考虑这里的细节了。
除此之外,本题还需要解决一个字符串解析的问题,要从单个域的表达式中提取出它所表达的数字,对此需要进行封装。
本题主要难点在于如何实现,下面给出一种简单清晰的写法。
实现细节
附上我另一个以前写的日期问题(模板):  2017省赛---日期问题(暴力 枚举)icon-default.png?t=N7T8https://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)

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

  • 42
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值