题目来源:http://118.190.20.162/view.page?gpid=T66
问题描述
试题编号: | 201712-3 |
试题名称: | Crontab |
时间限制: | 10.0s |
内存限制: | 256.0MB |
问题描述: | 样例输入 3 201711170032 201711222352 样例输出 201711170700 get_up |
------------------------------------------------------------
思路
超级麻烦的大模拟。
最简单的做法是从起始时间到结束时间遍历每一分钟,逐条检查这个时间点是否满足规则,如果满足就输出。但是这样有可能会超时(没试过,不知道会不会超时,但根据题目给的测试数据规模看很可能会超时)。
因此变通一下,以“天”为单位遍历开始那天到结束那天,对于每一天,逐条检查其是否符合规则中<day of month><month><day of week>三条要求,如果三条要求都满足,则根据<minutes><hours>的要求生成一天之内所有符合规则的时间点。求出符合规则的时间点后保存进结果数组。这时由于不是按时间顺序遍历,所以生成的crontab条目是乱序的,需要最后排序。
这个大模拟有两个重点:整体的数据结构、字符串操作。
1. 核心数据结构:
·std::set<int> rule[25][5]: 将规则中的分/时/日/月/星期几的信息存储在set中,方便查询(find)操作。用迭代器遍历set可以生成所有时间点
·bool is_star[25][3]:对于涉及日期遍历的日/月/星期几信息,如果是星号,则不用查询即可知道一定满足规则,故设置is_star数组作为查询捷径
·struct Event ansList[10005]:结果结构体数组。结构体Event记录时间信息、匹配到的第几条规则(用于输出结果时按时间和规则号排序)、command命令字符串,并具有按日期遍历功能
2. 字符串操作
出于运行速度的考虑没有使用STL string,只用了C语言的字符串处理函数。
strncpy字符串复制函数的用法值得一提。该函数头文件为"cstring",原型为
strncpy(char * Dst, char * Src, int n);
其中n为需要从Src中复制的字符串长度。使用时,建议将Dst字符串的长度设置为比需要复制的Src中的内容多一个char(用于存放结尾的’\0’),并将n设置为Dst的长度。下面一段代码可供参考:
char Src[20] = "Fri,Sat,Sun";
char Dst[4];
strncpy(Dst, Src+4, 4);
Dst[3] = '\0';
printf("%s", Dst);
3. 其他辅助功能
诸如“判断闰年”“根据日期计算是星期几”的函数,不再赘述。
------------------------------------------------------------
代码
// 遍历开始年初到结束年底的每一天,检查是否符合n条规则,
// 检查日月时用数组查找法
// 星期几单独检查,需要编写判断星期几的函数, 1970/01/01是thu
// 时分通过匹配到的规则中的时分数组生成
// 如果符合就放到结果结构体数组
// 最后连同起始时间、结束时间一起将结果数组排序,顺序输出排序结果
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<set>
const char MONTH_NAME[13][4] = {"", "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"};
const char WEEKDAY_NAME[7][4] = {"sun","mon","tue","wed","thu","fri","sat"};
const int MONTH_DAY[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int n = 0, cnt = 0;
bool is_star[25][3] = {}; // 各条规则中“日”“月”“星期几”是否是"*"
std::set<int> rule[25][5]; // 各条规则规定的时间条件数组,0-minutes, 1-hours, 2-day, 3-month, 4-week
char com[25][101]; // 各条规定的命令字符串
inline bool is_runnian(int year) // 判断是否是闰年
{
if (year % 100 == 0)
{
if (year % 400 == 0)
{
return true;
}
else
{
return false;
}
}
else if (year % 4 == 0)
{
return true;
}
else
{
return false;
}
}
struct Event {
int year, month, date, hour, minute;
int ind; // 匹配到的crontab命令的编号(用于输出排序)
char command[101];
Event(void): year(0), month(0), date(0), hour(0), minute(0), ind(-2)
{
strcpy(command, "");
}
Event(int yy, int mm, int dd, int hh, int min, int ii, char cc[101]):
year(yy), month(mm), date(dd), hour(hh), minute(min), ind(ii)
{
strcpy(command, cc);
}
Event(const Event & e):
year(e.year), month(e.month), date(e.date), hour(e.hour), minute(e.minute), ind(e.ind)
{
strcpy(command, e.command);
}
bool operator== (const Event & e) const
{
if (year == e.year && month == e.month && date == e.date && hour == e.hour && minute == e.minute && ind == e.ind
&& strcmp(command, e.command) == 0)
{
return true;
}
else
{
return false;
}
}
bool operator!= (const Event & e) const
{
if (year == e.year && month == e.month && date == e.date && hour == e.hour && minute == e.minute && ind == e.ind
&& strcmp(command, e.command) == 0)
{
return false;
}
else
{
return true;
}
}
bool operator< (const Event &e) const
{
if (year < e.year)
{
return true;
}
else if (year == e.year)
{
if (month < e.month)
{
return true;
}
else if (month == e.month)
{
if (date < e.date)
{
return true;
}
else if (date == e.date)
{
if (hour < e.hour)
{
return true;
}
else if (hour == e.hour)
{
if (minute < e.minute)
{
return true;
}
else if (minute == e.minute)
{
if (ind < e.ind)
{
return true;
}
}
}
}
}
}
return false;
}
void print() // 按题目要求的格式打印结果
{
printf("%04d%02d%02d%02d%02d %s\n", year, month, date, hour, minute, command);
}
void addOneDay() // 增加一天,用于遍历日期
{
int date_end;
if (is_runnian(year) && month == 2)
{
date_end = 29;
}
else
{
date_end = MONTH_DAY[month];
}
if (date == date_end)
{
if (month == 12)
{
year++;
month = 1;
date = 1;
}
else
{
month ++;
date = 1;
}
}
else
{
date ++;
}
}
int day_compare(const Event & e) const // 仅比较日期,用于日期遍历
{
if (year < e.year)
{
return -1;
}
else if (year > e.year)
{
return 1;
}
else
{
if (month < e.month)
{
return -1;
}
else if (month > e.month)
{
return 1;
}
else
{
if (date < e.date)
{
return -1;
}
else if (date > e.date)
{
return 1;
}
else
{
return 0;
}
}
}
}
} ansList[10005]; // 结果数组
int which_day (int year, int month, int day) // 输入日期计算星期几, 1970/01/01是星期一
{
int days = 0, i;
for (i=1970; i<year; i++)
{
if (is_runnian(i))
{
days += 366;
}
else
{
days += 365;
}
}
for (i=1; i<month; i++)
{
if (is_runnian(year) && i == 2)
{
days += 29;
}
else
{
days += MONTH_DAY[i];
}
}
days += day;
return (days+3)%7;
}
void solve (char str[101], int type, int ind) // 解析规则中含有的"*" "," "-" 和英文缩写
// type: 0-minutes, 1-hours, 2-day, 3-month, 4-week
{
std::set<int> ans;
int i = 0, j = 0, k = 0, len = strlen(str), num = 0, prenum = 0;
char numstr[4];
bool flag = false; // '-'号标记
int is_char = -1; // 字符串首字母的位置,如果不是字符串则 = -1
if (strcmp(str, "*") == 0)
{
if (type == 0)
{
for (j=0; j<60; j++)
{
ans.insert(j);
}
}
else if (type == 1)
{
for (j=0; j<24; j++)
{
ans.insert(j);
}
}
else
{
is_star[ind][type-2] = true;
}
}
else
{
num = 0;
for (i=0; i<len; i++)
{
if (str[i] >= '0' && str[i] <= '9')
{
num = num *10 + (str[i] - '0');
}
else if (str[i] == ',')
{
if (is_char != -1)
{
strncpy(numstr, str+is_char, 4);
numstr[3] = '\0';
if (type == 3)
{
for (k=1; k<=12; k++)
{
if (strcmp(numstr, MONTH_NAME[k]) == 0)
{
num = k;
}
}
}
else if (type == 4)
{
for (k=0; k<7; k++)
{
if (strcmp(numstr, WEEKDAY_NAME[k]) == 0)
{
num = k;
}
}
}
}
is_char = -1;
if (flag)
{
flag = false;
for (j=prenum; j<=num; j++)
{
ans.insert(j);
}
}
else
{
ans.insert(num);
}
num = 0;
}
else if (str[i] == '-')
{
if (is_char != -1)
{
strncpy(numstr, str+is_char, 3);
if (type == 3)
{
for (k=1; k<=12; k++)
{
if (strcmp(numstr, MONTH_NAME[k]) == 0)
{
num = k;
}
}
}
else if (type == 4)
{
for (k=0; k<7; k++)
{
if (strcmp(numstr, WEEKDAY_NAME[k]) == 0)
{
num = k;
}
}
}
}
is_char = -1;
prenum = num;
flag = true;
num = 0;
}
else if (str[i] >= 'a' && str[i] <= 'z')
{
if (is_char == -1)
{
is_char = i;
}
}
else if (str[i] >= 'A' && str[i] <= 'Z')
{
if (is_char == -1)
{
is_char = i;
}
str[i] -= 'A';
str[i] += 'a';
}
}
// 对结尾的处理等同于逗号
if (is_char != -1)
{
strncpy(numstr, str+is_char, 3);
if (type == 3)
{
for (k=1; k<=12; k++)
{
if (strcmp(numstr, MONTH_NAME[k]) == 0)
{
num = k;
}
}
}
else if (type == 4)
{
for (k=0; k<7; k++)
{
if (strcmp(numstr, WEEKDAY_NAME[k]) == 0)
{
num = k;
}
}
}
}
is_char = -1;
if (flag)
{
flag = false;
for (j=prenum; j<=num; j++)
{
ans.insert(j);
}
}
else
{
ans.insert(num);
}
num = 0;
}
rule[ind][type] = ans;
}
void generate_ans_through_days(int year, int month, int day) // 给定特定的一天,判断是否符合n条规则,生成结果
{
int ind = 0; // 规则号
bool match = true; // 这一天是否满足这条规则
std::set<int>::iterator it1, it2;
for (ind=0; ind<n; ind++)
{
match = true;
if (!is_star[ind][0])
{
if (rule[ind][2].find(day) == rule[ind][2].end())
{
match = false;
goto start_to_put;
}
}
if (!is_star[ind][1])
{
if (rule[ind][3].find(month) == rule[ind][3].end())
{
match = false;
goto start_to_put;
}
}
if (!is_star[ind][2])
{
if (rule[ind][4].find(which_day(year,month,day)) == rule[ind][4].end())
{
match = false;
goto start_to_put;
}
}
start_to_put: if (match)
{
for (it1 = rule[ind][0].begin(); it1 != rule[ind][0].end(); it1++)
{
for (it2 = rule[ind][1].begin(); it2 != rule[ind][1].end(); it2++)
{
Event ev(year, month, day, *it2, *it1, ind, com[ind]);
ansList[cnt++] = ev;
}
}
}
}
}
void through_days(Event start_time, Event end_time) // 遍历从起始日到终止日的所有日子
{
Event itev(start_time);
for (; itev.day_compare(end_time)!=1; itev.addOneDay())
{
generate_ans_through_days(itev.year, itev.month, itev.date);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("201712-3.txt", "r", stdin);
#endif
int i, j, i_start = 0, i_end = 0;
char str[101], s_year[4], s_month[2], s_date[2], s_hour[2], s_minute[2];
scanf("%d", &n);
scanf("%s", str);
strncpy(s_year, str, 4);
strncpy(s_month, str+4, 2);
strncpy(s_date, str+6, 2);
strncpy(s_hour, str+8, 2);
strncpy(s_minute, str+10, 2);
Event start_time(atoi(s_year), atoi(s_month), atoi(s_date), atoi(s_hour), atoi(s_minute), -1, "System_Start");
scanf("%s", str);
strncpy(s_year, str, 4);
strncpy(s_month, str+4, 2);
strncpy(s_date, str+6, 2);
strncpy(s_hour, str+8, 2);
strncpy(s_minute, str+10, 2);
Event end_time(atoi(s_year), atoi(s_month), atoi(s_date), atoi(s_hour), atoi(s_minute), -1, "System_End");
for (i=0; i<n; i++)
{
for (j=0; j<5; j++)
{
scanf("%s", str);
solve(str, j, i);
}
scanf("%s", com[i]);
}
through_days(start_time, end_time);
ansList[cnt++] = start_time;
ansList[cnt++] = end_time;
std::sort(ansList, ansList+cnt);
// 求start_time和end_time在ansList中的位置
for (i=0; i<cnt; i++)
{
if (ansList[i] == start_time)
{
i_start = i;
break;
}
}
for (i=cnt-1; i>=0; i--)
{
if (ansList[i] == end_time)
{
i_end = i;
break;
}
}
// 只输出start_time和end_time中间的结果
for (i=i_start+1; i<i_end; i++)
{
if (start_time < ansList[i] && ansList[i] < end_time)
{
ansList[i].print();
}
}
return 0;
}