cf 535D Tavas and Malekas (kmp)

传送门
题意就不叙述了
思路:
在这里插入图片描述
我们看下字符串重叠部分,我们必须让它相等对吧,而且题意要求我们只能用一个字符串去做拼接,这样就需要一个字符串的后面部分和它的前面部分相等,是不是想到了kmp。然后我们想想这个字符串ioioioio,我现在可以从位置1,3,5,7开始都可以,我们kmp求的是前后缀相等的最大长度。但是上面那个图我们看看,如果有重叠,需要s2开始到e1结束那一段,在原模式串中,前缀和后缀是相等的吧,那我们现在需要判断的就是在原模式串中,是否存在这样长度的前缀后缀呗。那我们kmp预处理一下原串,求得最大相等长度后,我们想要看看是否有更短的前缀后缀相等的字符串,因为有可能si在s2与e1间还有满足题意的,就不断迭代nex[]数组去求,其实这是个经典做法,我们来看看什么意思。
在这里插入图片描述

s0是原串,假设s1,s2是最长的前缀后缀相等的字符串,s3,s4也相等,但是是第二长的,也就是说s4和s0最后一段相等,s2也和s0最后一段相等,并且s2长度大于s4,那就是说s4和s2的最后一段长度相等是吧,现在s2和s1相等,那s4就和s1的最后一段相等是吧,标在上面了,是不是可以推出nex[strlen(s1)]=strlen(s3)(即s3,s4是以s1为母串的最长的前后缀相等的字符串,所以可以直接nex[]求出),如果还有比s4更长的,那证明s4就不是原先我们假设的第二长的了,岂不是矛盾了。所以,我们现在就可以用nex[]数组,一直迭代,不断求出仅此于上一个长度的且在原串中满足前后缀相匹配的字符串长度,这样,所有的满足前后缀匹配的子串长度都求出来了。

回到这道题目,我们每次只需要判断一下,如果新来的这个起点大于上一个终点,中间必然剩一些空,我随意填26个字母之一,如果小于等于,我们判断一下是否满足重叠,求一下需要重叠的长度,然后用预处理的结果判断这个长度是否可行,可行的话,就接着做下一个,最后必须要占位的字母数我们求出来了,剩下的k个随便填26个字母之一,就是26^k%mod。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=1e6+5;
#define ll long long
int n,m;
int len;
int p[N];
char s[N];
int nex[N];
int vis[N];
ll qpow(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
void ask_nex(){
    for(int i=2,j=0;i<=len;i++){
    	while(j&&s[i]!=s[j+1]) j=nex[j];
    	if(s[i]==s[j+1]) j++;
    	nex[i]=j;
	}
	int h=len;
	while(h){
		vis[h]=1;
		h=nex[h];
	}
}
int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    len=strlen(s+1);
    ask_nex();
    for(int i=1;i<=m;i++){
    	scanf("%d",&p[i]);
	}
	int zw;  //已占位
	if(m==0) {
	    zw=0;
	}
	else
	zw=len;
	for(int i=2;i<=m;i++){
		int s1=p[i-1];
		int s2=p[i];
		int e1=s1+len-1;
		int e2=s2+len-1;
		if(s2>e1){
		    zw+=len;
		}
		else{
			int tmp=e1-s2+1;
			if(vis[tmp]) zw+=(e2-e1);
		    else{
		    	puts("0");
		    	return 0;
			}
		}
	} 
	ll ans=qpow(26,n-zw);
	printf("%lld\n",ans);
	return 0; 
} 
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eeemmm123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值