LG2375/LOJ2246 「NOI2014」动物园 KMP改造

问题描述

LG2375

LOJ2246


题解

看了题解,需要回看,需要继续通过本题深入理解KMP。

为了将 \(\mathrm{KMP}\) 和只插入了一个模式串的\(\mathrm{AC}\)自动机有机统一,称通常意义下的 \(\mathrm{KMP}\)\(\mathrm{next}\) 数组为 \(\mathrm{fail}\)

通过对 \(\mathrm{num}\) 数组的观察,发现, \(\mathrm{num}\) 数组就是对于每一个前缀,求其公共不重叠前后缀的个数

由于只有一个串,通过 \(L \le 10^6\) 的线性复杂度的数据规模,可以猜出肯定和 \(\mathrm{KMP}\) 有关。

回顾 \(\mathrm{KMP}\)\(\mathrm{fail}\) 数组的定义,是对于每一个前缀,其 最长公共前后缀的长度

对于一个前缀 \(i\)\(fail[i]\) 是它的一个公共前后缀,那么 \(fail[fail[i]]\) 也是它的公共前后缀。

可以画图来理解一下:

同理, \(fail[fail[fail[i]]]\) ... 都是前缀 \(i\) 的公共前后缀。

于是通过这个办法递推一下前缀 \(i\)公共前后缀数目(允许重叠),这实际上就是 \(\mathrm{num}\) 数组的弱化版,即允许重叠的版本。

实际上,我们求出的这个弱化版数组,就是比实际上的 \(num\) 数组长了一点,所以做第二次 \(\mathrm{KMP}\) 的时候,只需要再让 \(j\)\(\mathrm{fail}\) ,直到它的一半比 \(i\) 小。

为什么是一半?显然 \(num_i \le \frac{i}{2}\)


\(\mathrm{Code}\)

#include<bits/stdc++.h>
using namespace std;

template <typename Tp>
void read(Tp &x){
	x=0;char ch=1;int fh;
	while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
	if(ch=='-') ch=getchar(),fh=-1;
	else fh=1;
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=fh;
}

const int maxn=1000000+7;
const int mod=1000000007;

int T,n;
char s[maxn];

int fail[maxn],num[maxn];

void Reset(){
	memset(fail,0,sizeof(fail));
}

void KMP(){
	num[1]=1;
	for(int i=2,j=0;i<=n;i++){
		while(j&&s[j+1]!=s[i]) j=fail[j];
		if(s[j+1]==s[i]) ++j;
		fail[i]=j;num[i]=num[j]+1;
	}
}

void solve(){
	KMP();
	long long ans=1;
	for(int i=2,j=0;i<=n;i++){
		while(j&&s[i]!=s[j+1]) j=fail[j];
		if(s[i]==s[j+1]) ++j;
		while((j<<1)>i) j=fail[j];
		ans=ans*(((long long)num[j]+1ll)%mod)%mod;
	}
	printf("%lld\n",ans);
}

int main(){
	read(T);
	while(T--){
		scanf("%s",s+1);n=strlen(s+1);
		solve();
	}
	return 0;
}
posted @ 2019-10-07 21:24  览遍千秋  阅读(179)  评论(0编辑  收藏  举报