[NOI2014]动物园(kmp+递推)

【题解】

这里首先定义失配指针f[i]满足:第i个位置的字符与第f[i]位相同,字符数组从1开始 
如:aba f[1]=0,f[2]=0,f[3]=1 
要求出num[i],只需延f指针上溯,找到所有长度不超过i/2的位置,它的数目即为num[i]
可以考虑fail树的思想,用cnt[i]记录从i延失配指针上溯,能遇到的结点数目 
找出最大的长度不超过i/2的位置j,则num[i]=cnt[j]+1
倍增法即可找出 
然而复杂度为:O( n*L*log(L) ),会TLE掉

只能从递推的角度考虑了 
设next[i]表示:从i延失配指针上溯遇到的第一个度不超过i/2的位置 
类似kmp再做一遍即可求出该数组 


【代码】

两遍KMP满分的:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MOD 1000000007ll
char s[1000005];
int f[1000005],cnt[1000005],next[1000005];
int main()
{
	long long ans;
	int n,L,i,j;
	scanf("%d",&n);
	for(;n>0;n--)
	{
		scanf("%s",s);
		L=strlen(s);
		for(i=L;i>=1;i--)
			s[i]=s[i-1];
		f[1]=0;
		for(i=1;i<L;i++)
		{
			j=f[i];
			while(s[j+1]!=s[i+1]&&j>0) j=f[j];
			if(s[j+1]==s[i+1]) f[i+1]=j+1;
			else f[i+1]=0;
		}
		cnt[0]=0;
		for(i=1;i<=L;i++)
			cnt[i]=cnt[f[i]]+1;
		next[1]=0;
		for(i=1;i<=L;i++)
		{
			j=next[i];
			if(j<<1==i) j=f[j];
			while(s[j+1]!=s[i+1]&&j>0) j=f[j];
			if(s[j+1]==s[i+1]) next[i+1]=j+1;
			else next[i+1]=0;
		}
		ans=1;
		for(i=1;i<=L;i++)
			ans=(ans*((long long)cnt[next[i]]+1LL))%MOD;
		printf("%lld\n",ans);
	}
	return 0;
}


另附一个倍增的,猥琐地TLE了3个点 TAT :

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MOD 1000000007ll
char s[1000005];
int f[1000005][25],cnt[1000005];
int main()
{
	long long ans;
	int n,L,i,j,k;
	scanf("%d",&n);
	for(;n>0;n--)
	{
		scanf("%s",s);
		L=strlen(s);
		for(i=L;i>=1;i--)
			s[i]=s[i-1];
		f[1][0]=0;
		for(i=1;i<L;i++)
		{
			j=f[i][0];
			while(s[j+1]!=s[i+1]&&j>0) j=f[j][0];
			if(s[j+1]==s[i+1]) f[i+1][0]=j+1;
			else f[i+1][0]=0;
		}
		cnt[0]=0;
		for(i=1;i<=L;i++)
			cnt[i]=cnt[f[i][0]]+1;
		for(i=1;i<=20;i++)
			for(j=1;j<=L;j++)
				f[j][i]=f[f[j][i-1]][i-1];
		ans=1;
		for(i=1;i<=L;i++)
		{
			j=f[i][0];
			while(j>i>>1)
			{
				for(k=1;f[j][k]>i>>1;k++);
				j=f[j][k-1];
			}
			ans=(ans*((long long)cnt[j]+1LL))%MOD;
		}
		printf("%lld\n",ans);
	}
	return 0;
}


你可能感兴趣的:(KMP,递推,fail树)