【ZOJ】3785 What day is that day? ——KMP 暴力打表找规律
转自:http://www.cnblogs.com/kevince/p/3887827.html
首先声明一下,这里的规律指的是循环,即找到最小循环周期。
这么一说大家心里肯定有数了吧,“不就是next数组性质的应用嘛”,没错,正是如此。
在ACM的比赛中有些时候会遇到一些题目,可以或必须通过找出数据的规律来编写代码,这里我们专门来讨论下 如何运用KMP中next数组的性质 来寻找一个长数组中的最小循环周期。
先来看一道题
ZOJ 3785
It's Saturday today, what day is it after 11 + 22 + 33 + ... + NN days?
Input
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:
There is only one line containing one integer N (1 <= N <= 1000000000).
Output
For each test case, output one string indicating the day of week.
Sample Input
2
1
2
Sample Output
Sunday
Thursday
Hint
A week consists of Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and Saturday.
题目的大意是知道今天是周六,让你求 f = 11 + 22 + 33 + ... + NN 这么多天之后是星期几。
也就是求f % 7对于每个输入的N的值。这题在网上一搜题解,都说是打表找规律,当然这题有两种找法,一是对于每个ii % 7 的值都找规律。
这里我们打表可知 前100个值如下所示
1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2 5 1 5 1 0 1 4 1 4 4 6 0 1 1 3 2 6 1 0 1 2 2 1 2 6 0 1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2 5 1 5 1 0 1 4 1 4 4 6 0 1 1 3 2 6 1 0 1 2 2 1 2 6 0 1 4 6 4 3 1 0 1 1 4 2 1 6 0 1 2
有一种找规律的方法是当有数字等于第一个数的时候做个标记,再人工判断是否能够构成一个循环。
不可否认的,对于周期较短的一组数字这样找周期并不难,可是如果周期大到数百数千甚至数万时,靠这种方法找周期恐怕是杯水车薪。
当时我就迷茫在了这一长串的数字中不知所措,猛然想起前不久看过的KMP中next数组的性质,当即想到了用KMP求最小重复子串长度的方法,于是脑洞大开……
该性质为:令j=leni-next[i],如果i%j==0且i/j>1,j就是Pi的最小循环节( Pi表示文本串的前i个字符,leni表示该字符串的长度,一般表示为leni = i + 1)
还有一种找规律的方法是直接对 f % 7 的值进行打表找规律,按照上述方法找到的周期为294,下面要做的就很简单了~
1 #include <iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 #define N 605 8 #define M 200 9 #define ll long long 10 using namespace std; 11 int i,j; 12 int T; 13 int l; 14 int a[N],sum[N]; 15 char s[N]; 16 int next[N]; 17 int fun(int n) 18 { 19 int re = 1; 20 for(int i=1;i<=n;i++){ 21 re*=n; 22 re%=7; 23 } 24 return re; 25 } 26 void ini() 27 { 28 a[0]=sum[0]=0; 29 a[1]=sum[1]=1; 30 s[0]='0'; 31 s[1]='1'; 32 for(i=2;i<=N-5;i++){ 33 a[i]=fun(i); 34 sum[i]+=a[i]+sum[i-1]; 35 sum[i]%=7; 36 s[i]=sum[i]+'0'; 37 } 38 s[i]='\n'; 39 //printf("%s\n",s); 40 // for(i=1;i<=M;i++){ 41 // printf(" %d %d %d\n",i,a[i],sum[i]); 42 // printf(" %d",sum[i]); 43 //} 44 return; 45 } 46 47 void ini2() 48 { 49 int j = -1, i = 0; 50 next[0] = -1; 51 while(i < N-5) 52 { 53 if(j == -1 || s[i] == s[j]) 54 { 55 56 i++; 57 j++; 58 next[i] = j; 59 } 60 else 61 { 62 j = next[j]; 63 } 64 } 65 } 66 67 void ini3() 68 { 69 for(int i = 589; i <= N-5; ++i) 70 { 71 int length = i - next[i]; //循环节的长度 72 if(i != length && i % length == 0) //如果有多个循环 73 { 74 printf("%d %d\n", i, i / length); 75 break; 76 } 77 78 } 79 } 80 81 int main() 82 { 83 int x; 84 int ans; 85 ini(); 86 //ini2(); 87 // ini3(); 88 //freopen("data.txt","r",stdin); 89 scanf("%d",&T); 90 //while(scanf("%d",&n)!=EOF) 91 while(T--) 92 { 93 scanf("%d",&x); 94 ans=sum[x%294]; 95 if(ans==0) printf("Saturday\n"); 96 else if(ans==1) printf("Sunday\n"); 97 else if(ans==2) printf("Monday\n"); 98 else if(ans==3) printf("Tuesday\n"); 99 else if(ans==4) printf("Wednesday\n"); 100 else if(ans==5) printf("Thursday\n"); 101 else if(ans==6) printf("Friday\n"); 102 } 103 return 0; 104 }
后来又练了一次,用了map
4172110 | 2016-03-15 15:26:39 | Accepted | 3785 | C++ | 820 | 280 | njczy2010 |
1 #include <iostream> 2 #include <cstdio> 3 #include <map> 4 #include <cstring> 5 #include <algorithm> 6 #include <cmath> 7 #include <string> 8 9 using namespace std; 10 11 #define N 1005 12 #define ll long long 13 #define mod 7 14 15 int TT; 16 int n; 17 int s[N]; 18 int next[N]; 19 map<int,string> mp; 20 21 void get_next() 22 { 23 int i,j; 24 i = 0; 25 j = -1; 26 next[0] = -1; 27 while(i<N) 28 { 29 if(j == -1 || s[i] == s[j]){ 30 i++;j++;next[i] = j; 31 } 32 else{ 33 j = next[j]; 34 } 35 } 36 } 37 38 int quickpow(int x,int n) 39 { 40 int re = 1; 41 while(n) 42 { 43 if(n&1){ 44 re = (re * x) % mod; 45 } 46 n /= 2; 47 x = (x * x) % mod; 48 } 49 return re; 50 } 51 52 void fi() 53 { 54 int i; 55 for(i = 2;i < N;i++){ 56 int length = i - next[i]; //循环节的长度 57 if(i != length && i % length == 0) //如果有多个循环 58 { 59 printf("%d %d %d\n", i, length, i / length); 60 break; 61 } 62 } 63 } 64 65 void ini() 66 { 67 int i; 68 s[0] = 0; 69 for(i = 1;i < N;i++){ 70 s[i] = (s[i - 1] + quickpow(i,i) )%mod; 71 } 72 mp[0] = "Saturday"; 73 mp[1] = "Sunday"; 74 mp[2] = "Monday"; 75 mp[3] = "Tuesday"; 76 mp[4] = "Wednesday"; 77 mp[5] = "Thursday"; 78 mp[6] = "Friday"; 79 //get_next(); 80 //fi(); 81 //293 82 } 83 84 int main() 85 { 86 //freopen("in.txt","r",stdin); 87 //freopen("out.txt","w",stdout); 88 ini(); 89 scanf("%d",&TT); 90 while(TT--){ 91 //while(scanf("%d",&n)!=EOF){ 92 scanf("%d",&n); 93 cout << mp[ s[ n%294 ] ] << endl; 94 } 95 return 0; 96 }