原作链接 :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 KBIt's Saturday today, what day is it after 11 + 22 + 33 + ... + NN days?
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).
For each test case, output one string indicating the day of week.
2
1
2
Sunday
Thursday
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)
关键代码如下:
/*注:int next[]为next数组,int arr[]为要找规律的数组,len为数组长度*/ next[0] = 0; for(int i = 1, q = 0; i < len; i++){ while(q > 0 && arr[i] != arr[q]) q = next[q-1]; if (arr[i] == arr[q]) q++; next[i] = q; if (q != 0 && (i + 1) % (i + 1 - q) == 0){ printf("%d\n", i+1-q); break; } }
可以求出最小周期为42。
还有一种找规律的方法是直接对 f % 7 的值进行打表找规律,按照上述方法找到的周期为294,下面要做的就很简单了~
AC代码如下:
1 #include <cstdio>
2
3 const char day[10][10] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"}; 4 int s[300]; 5
6 int work(int n) 7 { 8 int sum = 1; 9 for(int i = 1; i <= n; i++){ 10 sum = sum * n; 11 sum %= 7; 12 } 13 return sum; 14 } 15
16 void init() 17 { 18 s[0] = 0; 19 for(int i = 1; i <= 294; i++){ 20 s[i] = s[i-1] + work(i); 21 s[i] %= 7; 22 } 23 } 24
25 int main() 26 { 27 int T; 28 int n; 29 init(); 30 scanf("%d", &T); 31 while(T--){ 32 scanf("%d", &n); 33 n %= 294; 34 printf("%s\n", day[ s[n]]); 35 } 36 return 0; 37 }