【题解】洛谷P2375 [NOI2014] 动物园(KMP)

洛谷P2375:https://www.luogu.org/problemnew/show/P2375

思路

这道题可以说是完全刷新了本蒟蒻对KMP的理解

感觉对next数组的理解上升到一个新的高度

首先题目给出了next数组的定义

关于整个字串 next[len]为前缀与后缀相同的最长长度

我们给出一张从你谷题解中扒下来的图

我们可以观察到对于整个长度为len的字串a

next[len],next[next[len]],next[next[next[len]]]...全部都是满足前缀与后缀相同的子串

但是仅仅把这些加起来是不足以解决这道题的

因为题目要求的是不重叠 因此我们需要判断这个子串长度是否大于此前缀i长度的一半 我们设cnt数组为有重叠的 再从中筛选

因为考虑到时间效率 我们需要用求next数组的方法求num数组

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define maxn 1000010
#define mod 1000000007
ll cnt[maxn],next[maxn];
ll len,ans;
char a[maxn];
int T;
void kmp()
{
    ll j=0;
    for(ll i=2;i<=len;i++)
    {
        while(j&&a[i]!=a[j+1]) j=next[j];
        if(a[i]==a[j+1]) j++;
        next[i]=j;
        cnt[i]=cnt[j]+1;//递推公式 
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(cnt,0,sizeof(cnt));
        memset(next,0,sizeof(next));
        ans=1;//初始化 
        scanf("%s",a+1);
        len=strlen(a+1);
        cnt[1]=1;//初始化 
        kmp();
        ll j=0;
        for(ll i=2;i<=len;i++)
        {
            while(j&&a[i]!=a[j+1]) j=next[j];
            if(a[i]==a[j+1]) j++;
            while((j<<1)>i) j=next[j];//不断缩小范围直到小于原前缀i长度的一半 
            ans*=(cnt[j]+1);//统计ans 
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/BrokenString/p/9806559.html