Uvalive 3026 - Period(next数组的应用)
题目链接 https://cn.vjudge.net/problem/UVALive-3026
【题意】
给定长度为n的字符串,n<=1e6,求它每个周期前缀的最短循环节对应的循环次数。
【思路】
KMP算法中next数组的简单应用,比如说第二组样例,字符串”aabaabaabaab”的情况如下
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
p[i] | a | a | b | a | a | b | a | a | b | a | a | b | \0 |
next[i] | -1 | 0 | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
可以观察到当某个前缀s[0,i]是周期性的前缀当且仅当(i+1)%(next[i+1]-(i+1))==0,同时next[i+1]是要大于0,为什么是这样的呢,原因就在于next数组的体现的功能是什么了,next[i]表示的是s[0,i-1]中最大的相同前缀后缀长度,举个例子,假设下面是一个字符串,由五个部分构成,每部分对应一个子串,每个子串长度是len,总长度lens=5*len
字符串s | 0 | 1 | 2 | 3 | 4 |
---|
假设现在next[lens]=4*len,说明前缀s[0,1,2,3]和后缀s[1,2,3,4]完全相同,进一步可以得到s[0]=s[1]=s[2]=s[3]=s[4],也就是说循环节的长度就是lens-next[lens],当然要保证长度可以整除并且这个前缀不能为空,所以就有了刚刚的两个条件了,当某个前缀s[0,i]是周期性的前缀当且仅当(i+1)%(next[i+1]-(i+1))==0
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000050;
int lenp;
char p[maxn];
int next[maxn];
void getnext(){
next[0]=-1;
int k=-1,j=0;
while(j<lenp){
if(-1==k || p[k]==p[j]){
++k;
++j;
next[j]=k;
}
else k=next[k];
}
}
int main(){
int kase=0;
while(scanf("%d",&lenp)==1 && lenp){
scanf("%s",p);
getnext();
printf("Test case #%d\n",++kase);
for(int i=2;i<=lenp;++i){
if(next[i]>0 && i%(i-next[i])==0){
printf("%d %d\n",i,i/(i-next[i]));
}
}
puts("");
}
return 0;
}