【ZOJ】3785 What day is that day? ——KMP 暴力打表找规律

转自:http://www.cnblogs.com/kevince/p/3887827.html

首先声明一下,这里的规律指的是循环,即找到最小循环周期。

这么一说大家心里肯定有数了吧,“不就是next数组性质的应用嘛”,没错,正是如此。

在ACM的比赛中有些时候会遇到一些题目,可以或必须通过找出数据的规律来编写代码,这里我们专门来讨论下 如何运用KMP中next数组的性质 来寻找一个长数组中的最小循环周期。

先来看一道题

ZOJ 3785

What day is that day?

Time Limit: 2 Seconds      Memory Limit: 65536 KB

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 }

 

posted on 2014-08-23 09:46  njczy2010  阅读(446)  评论(0编辑  收藏  举报