BZOJ3670 [NOI2014]动物园 [KMP]
题目描述
题解
这道题主要利用了Fail数组(本题中的next数组)的性质。
熊猫:“对于字符串S的前i个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作next[i]。”
设 num[i] n u m [ i ] 表示字符串的前 i i 位中前缀和后缀相等的字符串个数(没有“不重叠”的限制)。显然有:,其中 +1 + 1 是因为字符串本身是自己的后缀。
显然,当指向模式串的指针 j j 在模式串后半段时,这个前缀一定与后缀重叠;而当 j j 指向模式串的前半段时,这个前缀一定不与后缀重叠。所以在计算时,只要保证 j∗2≤i j ∗ 2 ≤ i ,这样的 num[j] n u m [ j ] 就符合题目的要求。
代码
只比普通的KMP多两行代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 1000100
using namespace std;
ll num[N],Fail[N];char s[N];
ll KMP(){
ll len=strlen(s+1),Ans=1LL;
for(ll i=1;i<=len;i++)Fail[i]=0,num[i]=0;
num[1]=1LL;
for(ll i=2,j=0;i<=len;i++){
while(j && s[j+1]!=s[i])j=Fail[j];
if(s[j+1]==s[i])j++;Fail[i]=j;
num[i]=num[j]+1;
}
for(ll i=2,j=0;i<=len;i++){
while(j && s[j+1]!=s[i])j=Fail[j];
if(s[j+1]==s[i])j++;
while(j && j*2>i)j=Fail[j];
Ans=Ans*(num[j]+1)%(ll)(1e9+7);
}
return Ans;
}
int main(){
ll n;scanf("%lld",&n);
while(n--){
scanf("%s",s+1);
printf("%lld\n",KMP());
}
return 0;
}