hdu 1358 Period (KMP-----next数组)
Description
Input
Output
Sample Input
Sample Output
给你一个串,让你求出这个串循环的次数和地方
比如说Case 2的aabaabaabaab
在第2个位置a循环了2次 输出2 2
在第6个位置aab循环了2次 输出6 2
在第9个位置aab循环了3次 输出9 3
这个题其实就是求next数组外加找规律
先来看下这张图
其中,蓝色的为i位置前,前缀和后缀相同的部分,橙色为二者相交的部分
下面开始推导:
对于abcabc类串(只循环2次)
由于只循环2次,所以当i指向串的末尾的时候,next[i] = strlen(str)/2 (串的一半,正好前缀和后缀相等)
对于多次循环的串(如同aaaaaaaa,其中a循环了N次):
则一定能满足上图. 因为是多次循环,所以前缀或者后缀与整个串的差别只有用来循环的那个串
比如说aaaa,a循环了4次,i=4的时候前缀为aaa,后缀为aaa,与原始串aaaa只差了一个用来循环的a
综上,我们一定有当字符串开始第二个周期的时候,总串长减去蓝色的长度就为循环节的长度
换成公式就是:
记buf = i-next[i](这一步是求相同部分,因为必定以这个数字循环的串的长度)
if i%buf == 0 && i/buf > 1
则 i 为循环的一个位置,最短的循环的串的长度为buf,循环次数为i/buf(必须满足循环次数>=2)
式子有了代码就好写了
代码如下:
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>
#define For(i,n) for(int i=0;i<n;i++)
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn = 1e6+5;
int x;
char s2[maxn];
int nexta[maxn];
void Find_Next(){
nexta[0] = -1;
nexta[1] = 0;
int cnt = 0;
int i = 2;
while(i<=x){
if(s2[i-1]==s2[cnt])
nexta[i++] = ++ cnt;
else if(cnt>0)
cnt = nexta[cnt];
else
nexta[i++] = 0;
}
}
/*转载请保留此信息*/
/*hdu 1358 Period */
/*来源: 风灯记 */
/*https://blog.csdn.net/bestsort*/
int main() {
int cnt = 0;
while(cin >> x && x){
cnt ++;
mem(s2);
mem(nexta);
cin >> s2;
Find_Next();
cout << "Test case #" << cnt << endl;
For(i,x+1){
int buf = i-nexta[i];
if(i%buf==0 && i/buf > 1)
cout <<i << " " << i/buf << endl;
}
cout << endl;
}
return 0;
}