3 aaa 12 aabaabaabaab 0
Test case #1 2 2 3 3 Test case #2 2 2 6 2 9 3 12 4
题目大意:
给一个字符串,从第二个字符开始,让你判断前面字符串是否具有周期性,然后输出此位置和最大周期数。(周期要大于一)
对 KMP 算法的扩展应用。
从第 i 个字符开始 if(i%(i-next[i])==0),则前面字符串会有重复,其重复次数是 i /(i-next[i]) 。
这个公式不必死记,k=next[i] 表示i前面k-1个字符与总的字符串前k个字符相同 ,注意这里 不是k,因为构建next数组时要加一。
举个例子:abcabcabc 的下一位是9号位,next[9]=6,9-6=3(k表示abc),此时i=9,k=3。
#include<iostream> #include<cstring> #include<stdio.h> using namespace std; char str[1000005]; int Next[1000005]; int n; void init() { int i=0,j=-1; Next[0]=-1; while(i<n) { if(j<0||str[i]==str[j]) Next[++i]=++j; else j=Next[j]; } } int main() { int TIME=0; while(cin>>n&&n) { cin>>str; init(); cout<<"Test case #"<<++TIME<<endl; for(int i=2;i<=n;++i) { int k=i-(Next[i]); if(k!=i&&(i)%k==0) { cout<<i<<" "<<i/k<<endl; } } cout<<endl; } }
扩展一点:
这里构造next数组有两种方式:
一种是清华大学老师讲的:如我上面介绍的方法。
另一种是北大老师介绍的:
void get_next() { int i=1,j=0; next[1]=0; while(i<=d) { if(j==0||a[i]==a[j]) {i++;j++;next[i]=j;} else j=next[j]; } }这里记录数据起点不同,这位北大老师从数组坐标1开始储存数据。
本质是一样的。
部分引用:点击打开链接
#include<stdio.h> #include<string.h> int next[1000002],d; char a[1000002]; void get_next() { int i=1,j=0; next[1]=0; while(i<=d) { if(j==0||a[i]==a[j]) {i++;j++;next[i]=j;} else j=next[j]; } } int main() { int i,k,t=0,flag; while(1) { flag=1; memset(next,0,sizeof(next)); scanf("%d",&d); if(d==0) return 0; scanf("%s",a+1); get_next(); printf("Test case #%d\n",++t); // for(i=1;i<=d+1;i++) printf("%d ",next[i]); // printf("\n"); for(i=2;i<=d;i++) { k=i-(next[i+1]-1);<span style="white-space:pre"> </span>//相当于k=(i+1)-next[i+1];本质上与上一题一致 /*next[i+1]-1是i+1之前的循环长度 用i一减就是剩下的长度 如果剩下的长度为一个重复串的长度 则可以输出 否则不能输出 如 abcabcabc next[10]-1=6 用i一减(等于从abcabcabc中把abcabcabc拿走) 剩下个3(即abc) i是3的整数倍(长串的1到i的子串中是全由abc组成) 此时能输出 next[9]-1=2 说明剩下的不够一个重复串 即不能被i整除 不能输出*/ if(i!=k&&i%k==0) printf("%d %d\n",i,i/k);//i==k是指循环长度为0 即没有循环 } printf("\n"); } return 0; } /*非优化的next数组的含义是:next[i]=k模式串下标为i的字符的前k-1个字符与开首的前k-1个字符相等 例如 aabaabaab next[10]=7 表示下标为10之前的字符前6个字符 与开首的前6个字符相等 所以当匹配不成功时 不用回溯到下标为6的位置去继续匹配 */