P2375-[NOI2014]动物园【KMP,字符串】

正题

评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P2375


题目大意

对于kmp操作,我们多求一个 n u m 数组,表示对于字符串 S 的前 i 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠。


解题思路

我们可以在求next的时候进行递推,然后就可以求出可以重合的num。
然后我们在用next去掉不可以的答案。
但是如果这样我们就会超时,所以我们发现如果不重合,那么长度最大为 n ÷ 2 ,所以我们直接不更新 j ,因为这个位置满足长度为 i ,那么也一定满足长度为 i + 1 的情况。


code

#include<cstdio>
#include<cstring>
#define mod 1000000007
using namespace std;
char s[1000011];
int l,p[1000011],ans[1000001],tot,t;
void ycl()//求next数组
{
    ans[0]=0;ans[1]=1;
    for (int i=1,j=0;i<l;i++)
    {
        while (j&&s[i]!=s[j]) j=p[j];
        if (s[i]==s[j]) j++;
        ans[i+1]=ans[j]+1;//递推
        p[i+1]=j;
    }
}
long long answer()
{
    long long sum=1;
    for (int i=1,j=0;i<l;i++)
    {
        while (j&&s[i]!=s[j]) j=p[j];
        if (s[i]==s[j]) j++;
        while((j<<1)>(i+1)) j=p[j];//计算j位置
        sum=(sum*(long long)(ans[j]+1))%mod;
    }
    return sum;
}
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        memset(p,0,sizeof(p));
        scanf("%s",s);
        l=strlen(s);
        ycl();
        printf("%lld\n",answer());
    }
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/81782227