CF535D Tavas and Malekas

Mean

文本串长度为 \(n\),给你模式串在文本串中出现的 \(m\) 个位置(必须在这些位置有出现过,也可以在其他位置出现),求可能的文本串数量。

Sol

\(KMP\)

首先,最后答案一定与空位置的数目\(cnt\)相关,观察发现是\(26^{cnt}\)

考虑相邻的两个位置\(p1,p2\)

如果\(p1,p2\)不相交,则直接覆盖即可。

如果\(p1,p2\)相交,则需要检验重叠部分是否相同,即检验是否存在相同前后缀的长度与重叠部分长度相等。不存在则非法。

如果存在位置\(p3\)\(p3\)\(p1,p2\)相交,如果\(p3与p2\)相交合法,\(p1\)\(p2\)相交合法,那么\(p3\)\(p1\)相交一定也合法。(画图理解)所以只要判断相邻两个状态是否合法即可。

发现了这点后就可以用差分+\(KMP\)来解决了.

需要考虑\(m=0\)的特判。

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
const ll mod = 1e9+7;
int n,m;
char t[N],p[N];
int next1[N];
int q[N];

void get_next(char p[],int lenp){
	int j=0;
	for(int i=2;i<=lenp;++i){
		while(j&&p[j+1]!=p[i])j=next1[j];
		if(p[j+1]==p[i])j++;
		next1[i]=j;
	}
}
void kmp(char t[],int lent,char p[],int lenp){
	int j=0;
	for(int i=1;i<=lent;++i){
		while(j&&p[j+1]!=t[i])j=next1[j];
		if(p[j+1]==t[i])j++;
		if(j==lenp){
			cout<<i-lenp+1<<endl;//输出p在t中出现的位置
		}
	}
}
int vis[N];
ll qpow(ll a,ll b){
	ll ans = 1;
	while(b){
		if(b&1){
			ans = (ans*a)%mod;
		}
		a = (a*a)%mod;
		b>>=1;
	}
	return ans;
}
int mp[N];

int main(){
	scanf("%d %d",&n,&m);
	scanf("%s",p+1);
	int lenp=strlen(p+1);
	get_next(p,lenp);
	for(int i=1;i<=m;++i){
		scanf("%d",&q[i]);
	}
	int pos = lenp;
	while(next1[pos]){	
		vis[next1[pos]]=1;
		pos = next1[pos];
	}	
	if(m==0){
		printf("%lld\n",qpow(26,n));
		return 0;
	}
	else if(m==1){
		printf("%lld\n",qpow(26,n-lenp));
		return 0;
	}
	for(int i=2;i<=m;++i){
		if(q[i]-q[i-1]>=lenp){//no cross
			mp[q[i]]++;
			mp[q[i]+lenp]--;
			mp[q[i-1]]++;
			mp[q[i-1]+lenp]--;
			continue;
		}
		else{//cross
			if(vis[q[i-1]+lenp-q[i]]==0){
				printf("0\n");return 0;
			}
			mp[q[i]]++;
			mp[q[i]+lenp]--;
			mp[q[i-1]]++;
			mp[q[i-1]+lenp]--;
		}
	}	

	ll cnt=0;
	for(int i=1;i<=n;++i){
		mp[i]+=mp[i-1];
	}
	for(int i=1;i<=n;++i){
		if(mp[i]==0)cnt++;
	}
	printf("%lld\n",qpow(26,cnt));
	return 0;
}