CCF 201712-3 Crontab

题目来源:http://118.190.20.162/view.page?gpid=T66

问题描述

试题编号:

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

 

------------------------------------------------------------

思路

超级麻烦的大模拟。

最简单的做法是从起始时间到结束时间遍历每一分钟,逐条检查这个时间点是否满足规则,如果满足就输出。但是这样有可能会超时(没试过,不知道会不会超时,但根据题目给的测试数据规模看很可能会超时)。

因此变通一下,以“天”为单位遍历开始那天到结束那天,对于每一天,逐条检查其是否符合规则中<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;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值