next数组的应用,在做这题之前本以为自己掌握了kmp算法的精髓,但是在拿到这道题的时候竟然还是被卡了几个小时,真是伤心欲绝,
于是再次重温了next数组的本质和性质。这里引用别人总结的知识点来讲解一下吧
毕竟自己还是讲不清楚。
给定一个字符串s,从小到大输出s中既是前缀又是后缀的子串的长度。
此题非常简单,借用KMP算法的next数组,设s的长度为n,则s串本身必定满足条件。其他满足条件的子串都有个特征,就是该子串的最后一个字符肯定与s的最后一个字符相同。这正是next数组发挥作用的时候。从n - 1位既最后一位开始回滚,若s[next[n-1]] == s[n-1],则子串s[0,1,2,...,next[n-1]]是满足条件的子串。然后判断s[next[next[n-1]]] == s[n-1]是否成立,这样一直回滚,直到next[next[.....next[n-1]]] == -1为止。把答案从大到小存下来,再从小到大输出即可。
大致思路:
如左图,假设黑色线来代表字符串str,其长度是len,红色线的长度代表next[len],根据next数组定义易得前缀的next[len]长度的子串和后缀next[len]长度的子串完全相同(也就是两条线所对应的位置)。我们再求出next[len]位置处的next值,也就是图中蓝线对应的长度。同样可以得到两个蓝线对应的子串肯定完全相同,又由于第二段蓝线属于左侧红线的后缀,所以又能得到它肯定也是整个字符串的后缀。
所以对于这道题,求出len处的next值,并递归的向下求出所有的next值,得到的就是答案。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<limits.h>
#include<math.h>
#include<algorithm>
using namespace std;
char a[400005];
int p[400005],n,m;
int sum[400005];
void print()
{
int i,j=0;
p[1]=0;
for(i=2;i<=n;i++)
{
while(j>0 && a[j+1]!=a[i])
j=p[j];
if(a[j+1]==a[i])
j++;
p[i]=j;
}
int k=0;
for(i=n;i!=0;)
{
sum[++k]=p[i];
i=p[i];
}
for(i=k-1;i>=0;i--)
if(sum[i]!=0)
printf("%d ",sum[i]);
printf("%d\n",n);
}
int main()
{
int i;
while(scanf("%s",a+1)!=EOF)
{
n=strlen(a+1);
print();
}
}