题目
试题编号: 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 a
except:
for i in range(12):
if (m.lower() == Month_string_list[i].lower()): # 使用.lower()函数转换为小写
return i + 1
def w_string2int(w): # 将所有星期全部换为数字
try:
a = int(w)
return a
except:
for i in range(7):
if (w.lower() == Weekday_string_list[i].lower()): # 使用.lower()函数转换为小写
return i
def ddmm(day, mon): # 闰年的2月29天,1 3 5 7 8 10 12 31days ,其他30days
# d为输入天的字符串,m为输入月份的字符串
# start为开始的天,end为结束的天
# 函数返回所有符合条件的天的list
day_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的情况避免重复输出3
mons = 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的情况避免重复输出3
for x in monset:
for y in dayset:
day_mon_set.append(x * 100 + y)#11月12日就是1112
return 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的情况避免重复输出3
hhs = 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的情况避免重复输出3
for h in hourset:
for m in minset:
min_hour_set.append(h * 100 + m)
return min_hour_set
def 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的情况避免重复输出3
else:
dset.append(w_string2int(w))
dset = list(set(dset))
return dset
def 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)):
continue
else:#不是闰年
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 normal
n, 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])