题目
试题编号: 201712-3
试题名称: Crontab
时间限制: 10.0s
内存限制: 256.0MB
样例输入
3 201711170032 201711222352
0 7 * * 1,3-5 get_up
30 23 * * Sat,Sun go_to_bed
15 12,18 * * * have_dinner
样例输出
201711170700 get_up
201711171215 have_dinner
201711171815 have_dinner
201711181215 have_dinner
201711181815 have_dinner
201711182330 go_to_bed
201711191215 have_dinner
201711191815 have_dinner
201711192330 go_to_bed
201711200700 get_up
201711201215 have_dinner
201711201815 have_dinner
201711211215 have_dinner
201711211815 have_dinner
201711220700 get_up
201711221215 have_dinner
201711221815 have_dinner
题目分析(个人理解)
- 首先此题是基于字符串处理的模拟题,根据题目的描述,我们要对输入的所有内容进行标准化,星期,月份都可以用任意大小写的字母的缩写或者数字表示具体内容,由于输入的第一行也就是开始时间和结束时间采用数字表示,所以,我将在后面输入的命令也全部标准化为数字,注意星期从0(表示周天)开始,到6。月份从1开始到12。
- 由于本题处理内容较多,我采用函数对方法封装,对于处理星期和月份标准化的函数,如果实参是int则直接返回,如果是英文字母则全部转换为小写之后可以根据位序返回具体的值。
- 接下来处理命令,对于命令有以下一些特殊字符,逗号表示并的关系,-表示开始结束(如果是星期的位置,1,3-5表示周一,周三,周四 ,周五),星号表示所有。(月份同理)尤其注意重复的情况,比如:3,1-5需要进行去重,不然后面会出现重复输出的情况,由于我全部用列表存储,那么可以直接用list(set())去重。(元组值的唯一性),分钟小时星期是同样的标准化方法。
- 标准化完成后,要进行判断,判断命令是否在第一行输入的给定的时间范围之内,数字化标准后,只需要用数值判断范围即可。如果命令的时间不在此区间则直接跳过判断下一个命令,还要判断每个月的日期是否正常,比如11月31日这种,还要判断星期是否正常:比如2023年11月18日是周五(实际上是周六)。
- 对于日期数是否正常,只用判断该日期是否小于这个月的最大日期数,由于2月因为闰年有特殊性,因此分为闰年和非闰年分别判断,对于星期,我们根据题目条件已知1970年1月1日是周四,所以我们可以推算出对应的星期(已知年月日推星期,这我在前面的打卡中也遇到过此类题目)。只需算出19700101到命令的执行日期的总天数对7取余数(算出多少周)再加四,再对7取余即可得知星期。我们扩展一下,如果不给1970年1月1日是周四阁下该如何应对?大家可以搜一下蔡勒公式。
- 上代码!!!
# crontab
Days_of_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]#不是闰年
Days_of_mons_in_leap_year = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]#闰年
Month_string_list = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
Weekday_string_list = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']def m_string2int(m): # 将所有月份全部转化为数字try:a = int(m)return aexcept:for i in range(12):if (m.lower() == Month_string_list[i].lower()): # 使用.lower()函数转换为小写return i + 1def w_string2int(w): # 将所有星期全部换为数字try:a = int(w)return aexcept:for i in range(7):if (w.lower() == Weekday_string_list[i].lower()): # 使用.lower()函数转换为小写return idef ddmm(day, mon): # 闰年的2月29天,1 3 5 7 8 10 12 31days ,其他30days# d为输入天的字符串,m为输入月份的字符串# start为开始的天,end为结束的天# 函数返回所有符合条件的天的listday_mon_set = []dayset = []days = day.split(',') # 逗号是并的关系for d in days:if (d == '*'):dayset = list(i + 1 for i in range(31))elif (len(d.split('-')) != 1):s, e = list(map(int, d.split('-')))for i in range(s, e + 1):dayset.append(i)else:dayset.append(int(d))dayset=list(set(dayset))#去重,如果出现3,1-5的情况避免重复输出3mons = mon.split(',')#逗号是并的关系monset = []for m in mons:if (m == '*'):monset = list(i + 1 for i in range(12))elif (len(m.split('-')) != 1):s, e = m.split('-')for i in range(m_string2int(s), m_string2int(e) + 1):monset.append(i)else:monset.append(m_string2int(m))monset=list(set(monset))#去重,如果出现3,1-5的情况避免重复输出3for x in monset:for y in dayset:day_mon_set.append(x * 100 + y)#11月12日就是1112return day_mon_set#返回月日的格式def MMHH(mm, hh):#分钟小时标准化min_hour_set = []minset = []hourset = []mms = mm.split(',')for m in mms:if (m == '*'):minset = list(i for i in range(60))elif (len(m.split('-')) != 1):s, e = list(map(int, m.split('-')))for i in range(s, e + 1):minset.append(i)else:minset.append(int(m))minset = list(set(minset)) # 去重,如果出现3,1-5的情况避免重复输出3hhs = hh.split(',')for h in hhs:if (h == '*'):hourset = list(i for i in range(24))elif (len(h.split('-')) != 1):s, e = list(map(int, h.split('-')))for i in range(s, e + 1):hourset.append(i)else:hourset.append(int(h))hourset = list(set(hourset)) # 去重,如果出现3,1-5的情况避免重复输出3for h in hourset:for m in minset:min_hour_set.append(h * 100 + m)return min_hour_setdef day_of_week(ww):#标准化星期if (ww == '*'):return list(i for i in range(7))else:dset = []wws = ww.split(',')for w in wws:if (len(w.split('-')) != 1):s, e = w.split('-')for i in range(w_string2int(s), w_string2int(e) + 1):dset.append(i)dset = list(set(dset)) # 去重,如果出现3,1-5的情况避免重复输出3else:dset.append(w_string2int(w))dset = list(set(dset))return dsetdef check(ymhdmw):#检查给定的开始时间和事件进行的时间之间是否是包含的关系normal = []for X in ymhdmw:year, mon_day, hour_min, weekday, com = X# 检查是否在设置的时间范围内time = year * 100000000 + mon_day * 10000 + hour_min#标准化if (time < start or time >= end):continue# 检查每个月的日期数是否正常if ((year % 4) == 0):#闰年daysbefore = sum(Days_of_mons_in_leap_year[0:mon_day // 100 - 1])#计算到前一个月总共有多少天,比如:1116,计算的就是从1月到10月总共的天数if (Days_of_mons_in_leap_year[mon_day // 100 - 1] < (mon_day % 100)):continueelse:#不是闰年daysbefore = sum(Days_of_month[0:mon_day // 100 - 1])if (Days_of_month[mon_day // 100 - 1] < (mon_day % 100)):continue# 检查星期数是否正常years_from_1970 = year - 1970#1940到输入的年份有多少年num_of_leap_year = (years_from_1970 + 1) // 4#计算1970年之后有多少个闰年days_from_197011 = years_from_1970 * 365 + num_of_leap_year + daysbefore + mon_day % 100 - 1#计算1970年1月1日至输入的那天有多少天if (((days_from_197011 % 7) + 4) % 7 == weekday):#题目给了1976年1月1日是周四,接下来推算即可,可以判断某年某月某日是不是某星期normal.append([time, command])#将判断符合逻辑的时间和命令写入列表return normaln, start, end = list(map(int, input().split()))
crontab = []
startyear = start // 100000000
endyear = end // 100000000
for i in range(n):minutes, hours, day_of_month, month, dayofweek, command = input().split()minhoueset = MMHH(minutes, hours)daymonset = ddmm(day_of_month, month)week = day_of_week(dayofweek)ymhdmw = []for i in range(startyear, endyear + 1):for mh in minhoueset:for dm in daymonset:for w in week:ymhdmw.append([i, dm, mh, w, command])crontab.append(check(ymhdmw))
crontab1 = []
for X in crontab:for x in X:crontab1.append(x)
crontab2 = sorted(crontab1, key=lambda x: x[0])
for i in crontab2:print(i[0], i[1])