CCF-CSP Crontab
非常坑的一题,可能是因为我菜吧。
考的的东西很多,把几种模拟题都揉在一起了,比如:
- 日期处理,包括枚举分钟,计算某天是星期几,后者可以枚举天,也有公式,但是我记不住,哈哈。
- 字符串解析,这个不用说了,大模拟必考。
- 解析后建立满足题目意思的数据结构。
思路:首先注意日期的表示法,我用的是年月日时分表示法,而不是弄成一个整数,实际上我只会这一种表示法,并且在算法笔记上学过练过。然后把控制表(即Crontab)输入,解析,建立可以方便的查询的数据结构。还是老办法,用enum标记每种情况,然后不同情况不同处理。这里我分了三种情况:
4. Star,即任意值。
5. EQL,必须严格相等的一个值(单个值)。
6. RNG,可以多个值,每个值可以是范围。就是最复杂的情况,可以有逗号和横杠。注意到,单个值其实可以表示成横杠表示法,即4 = 4-4
,这样就统一了,不用再嵌套一个区分区间和严格相等的结构。
然后比较坑的就是计算某天是星期几了,这是我最怕的一个,因为老是算错。还好题目给了提示,19700101是星期四,然后笨办法累加得出s和t的星期,其实只需要计算s的星期即可。然后就没什么难点了,就是代码量略大,因为糅合了日期处理和字符串解析的代码。
#include <cstdio>
#include <vector>
#include <utility>
#include <cassert>
#include <string>
#include <cstring>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define MAXL 105
char line[MAXL];
bool IsLeap(int year) {
return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}
const int Day[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
const char MonName[][5]={"","jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"};
const char WeekName[][5]={"sun","mon","tue","wed","thu","fri","sat"};
int MonthDay(int year, int mon) {
// 注意,闰年的二月加一天。
return Day[mon]+(mon==2&&IsLeap(year));
}
int GetWeek(int y, int m, int d) {
int yy=1970;
int mm=1;
int dd=1;
int ww=4;
// 注意,这里出bug了,条件是或,不是与。
while (yy<y || mm<m || dd<d) {
++dd;
ww=(ww+1)%7;
if (dd==MonthDay(yy, mm)+1) {
dd=1;
++mm;
}
if (mm==13) {
mm=1;
++yy;
}
}
return ww;
}
struct Time {
int y, mon, d, h, min;
int w; // week
void Read() {
scanf("%04d%02d%02d%02d%02d",&y,&mon,&d,&h,&min);
w=GetWeek(y, mon, d);
// printf("w %d\n", w);
}
void Print() {
printf("%04d%02d%02d%02d%02d",y,mon,d,h,min);
}
friend bool operator==(const Time& a, const Time& b) {
return a.y==b.y &&
a.mon==b.mon &&
a.d==b.d &&
a.h==b.h &&
a.min==b.min;
}
void inc() {
// 自增1分钟。
min++;
if (min==60) {
min=0;
h++;
}
if (h==24) {
h=0;
++d;
w=(w+1)%7;
}
if (d==MonthDay(y, mon)+1) {
d=1;
++mon;
}
if (mon==13) {
mon=1;
++y;
}
}
};
enum {
STAR,
EQL, // equal
RNG, // range.
};
const char Name[][10]={"star","eql","rng"};
typedef pair<int,int> Range;
struct Cond {
int type;
int eql; // EQL.
vector<Range> rng;
bool Match(int val) {
int i;
switch (type) {
case STAR:return true;
case EQL:return eql==val;
default:
for (i=0;i<rng.size();++i) {
Range p=rng[i];
if (p.first<=val&&val<=p.second) {
return true;
}
}
return false;
}
}
};
#define MAXC 5
struct Node {
// 所有的条件。
Cond conds[MAXC];
char cmd[MAXL];
Node(){
}
bool Match(const Time& t) {
return conds[0].Match(t.min) &&
conds[1].Match(t.h) &&
conds[2].Match(t.d) &&
conds[3].Match(t.mon) &&
conds[4].Match(t.w);
}
};
vector<Node> tab;
// 把从i开始的字符串转换为数字,考虑数字和缩写混合的情况。
int Parse3(char str[], int& i, int len) {
if (isdigit(str[i])) {
int n=0;
while (i<len && isdigit(str[i])) {
n=n*10+(str[i]-'0');
++i;
}
return n;
}
string buf;
while (i<len && isalpha(str[i])) {
buf.push_back(tolower(str[i]));
++i;
}
for (int i=1;i<=12;++i) {
if (buf == MonName[i]) {
return i;
}
}
for (int i=0;i<=6;++i) {
if (buf == WeekName[i]) {
return i;
}
}
return -1;
}
void Parse2(Cond& cd, char str[], int len) {
if (len==1 && str[0]=='*') {
cd.type=STAR;
return;
}
bool rng=false;
// 看是否为单独取值。
for (int i=0;i<len;++i) {
if (str[i]==','||str[i]=='-') {
rng=true;
break;
}
}
if (!rng) {
// eql
cd.type=EQL;
int i=0;
cd.eql=Parse3(str, i, len);
assert(i==len);
return;
}
cd.type=RNG;
// 逗号分割的列表。
int i=0;
while (i<len) {
int b=Parse3(str, i, len);
int e=b;
if (str[i] == '-') {
++i;
e=Parse3(str, i, len);
}
Range r={b,e};
cd.rng.push_back(r);
if (i>=len) {
break;
}
if (str[i]==',') {
++i;
}
}
}
void Parse(Node& node) {
for (int i=0;i<MAXC;++i) {
char str[MAXL];
scanf("%s", str);
Parse2(node.conds[i], str, strlen(str));
}
scanf("%s", node.cmd);
}
int N;
void Print() {
for (int i=0;i<N;++i) {
printf("%s", tab[i].cmd);
for (int j=0;j<MAXC;++j) {
Cond& cd=tab[i].conds[j];
int type=cd.type;
printf(" %s", Name[type]);
if (type==EQL) {
printf(" %d", cd.eql);
} else if (type==RNG) {
for (int k=0;k<cd.rng.size();++k) {
Range r=cd.rng[k];
printf(" <%d,%d>", r.first,r.second);
}
}
}
puts("");
}
}
int main(int argc, char** argv) {
scanf("%d",&N);
Time a, b;
a.Read();
b.Read();
tab.resize(N);
for (int i=0;i<N;++i) {
Parse(tab[i]);
}
for (;!(a==b);a.inc()) {
for (int i=0;i<N;++i) {
if (tab[i].Match(a)) {
a.Print();
printf(" %s\n", tab[i].cmd);
}
}
}
return 0;
}