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;
}