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\n",ans); } return 0; }