BZOJ3670 [NOI2014]动物园 [KMP]

BZOJ3670 [NOI2014]动物园 [KMP]

题目描述

题目传送门

题解

这道题主要利用了Fail数组(本题中的next数组)的性质。

熊猫:“对于字符串S的前i个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作next[i]。”

n u m [ i ] 表示字符串的前 i 位中前缀和后缀相等的字符串个数(没有“不重叠”的限制)。显然有: n u m [ i ] = n u m [ F a i l [ i ] ] + 1 ,其中 + 1 是因为字符串本身是自己的后缀。

显然,当指向模式串的指针 j 在模式串后半段时, [ 1 , j ] 这个前缀一定与后缀重叠;而当 j 指向模式串的前半段时, [ 1 , j ] 这个前缀一定不与后缀重叠。所以在计算时,只要保证 j 2 i ,这样的 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;
} 

猜你喜欢

转载自blog.csdn.net/ArliaStark/article/details/81347721