把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ3670】[NOI2014] 动物园(KMP)

点此看题面

大致题意: 定义\(num_i\)为一个长度为\(n\)的字符串前\(i\)个字符构成的子串中同时是其后缀和前缀该后缀和前缀不重叠的字符串个数,求\(\prod_{i=1}^n(num_i+1)\)

\(KMP\)

首先,让我们不考虑后缀和前缀不重叠这个限制。

则满足条件的字符串,显然就是\(nxt_i,nxt_{nxt_i},nxt_{nxt_{nxt_i}}......\)

如果我们设这样的字符串有\(f_i\)个,则显然我们可以在求\(nxt\)数组的同时,递推得到\(f\)数组。(只要在\(nxt_i=j\)这句话之后加上\(f_i=f_{nxt_i}+1\)即可)

然后我们考虑加上这个限制后,其实就是求出最大的\(j\),满足\(j=nxt_{..._{nxt_i}}\),且\(2\times j\le i\),然后\(num_i\)就等于\(f_j\)

至于如何在枚举\(i\)的时候维护这个\(j\),只要以类似于求\(nxt\)数组的方法,并强制\(2\times j\le i\)即可。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define X 1000000007
using namespace std;
int n,nxt[N+5],f[N+5];char s[N+5];
I void GetNxt()//求nxt数组
{
	RI i,j;for(i=2,j=nxt[1]=0,f[1]=1;i<=n;++i)
	{
		W(j&&s[j+1]^s[i]) j=nxt[j];s[j+1]==s[i]&&++j,
		f[i]=f[nxt[i]=j]+1;//同时求出f数组
	}
}
I void GetAns()//求答案
{
	RI i,j,t=1;for(i=2,j=0;i<=n;++i)
	{
		W(j&&s[j+1]^s[i]) j=nxt[j];s[j+1]==s[i]&&++j;
		W(2*j>i) j=nxt[j];t=1LL*t*(f[j]+1)%X;//强制2*j<=i
	}printf("%d\n",t);
}
int main()
{
	RI Tt;scanf("%d",&Tt);W(Tt--) scanf("%s",s+1),n=strlen(s+1),GetNxt(),GetAns();
	return 0;
}
posted @ 2020-02-01 12:02  TheLostWeak  阅读(173)  评论(0编辑  收藏  举报