P2375 [NOI2014]动物园

传送门

如果不考虑不重叠的限制的话,$num[i]$ 是很容易求的:

scanf("%s",s+1);
int j=0; n=strlen(s+1); num[1]=1;
for(int i=2;i<=n;i++)
{
    while(j&&s[j+1]!=s[i]) j=f[j];
    f[i]= s[j+1]==s[i] ? ++j : 0;
    num[i]=num[j]+1;
}

对于限制,考虑每个点都暴力跳 $fail$,直到跳到合法为止

for(int i=1,j=i;i<=n;i++,j=i)
{while(j>i/2) j=f[j];
    ans=1ll*ans*(num[j]+1)%mo;
}

但是这种做法可以被卡到 $n^2$($aaaaaaaaaa...$ 这样的数据)

考虑 $KMP$ 时怎么维护 $fail$ 的,搞一个指针 $j$,指向当前上一个位置的 $fail$,这样就不用每次都重新跳了

那我们维护 $num$ 也可以考虑这种操作,然后复杂度就是 $O(n)$

具体看代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
typedef double db;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2e6+7,mo=1e9+7;
int n,f[N],num[N],ans=1;
char s[N];
int main()
{
    int T=read();
    while(T--)
    {
        scanf("%s",s+1);
        int j=0; n=strlen(s+1); ans=num[1]=1;
        for(int i=2;i<=n;i++)
        {
            while(j&&s[j+1]!=s[i]) j=f[j];
            f[i]= s[j+1]==s[i] ? ++j : 0;
            num[i]=num[j]+1;
        }
        j=0;
        for(int i=2;i<=n;i++)
        {
            while(j&&s[j+1]!=s[i]) j=f[j];
            if(s[j+1]==s[i]) j++;
            while(j>i/2) j=f[j];
            ans=1ll*ans*(num[j]+1)%mo;
        }
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/11334536.html