CF494B Obsessive String(KMP+DP)

这道题的题目真是太难读了。。。推了半天样例。其实就是让你求合法的集合数目。合法的集合定义为:

1、集合中的所有串都是s的子串,且互不重叠 2、集合中的所有串都含有子串t。

设串s长度为n,串t长度为m,我们首先用kmp在s中匹配t,匹配成功的位置我们打下标记flag[i]=1(s[i-m+1...i]=t[1..m]).

以下角标均针对串s:设dp[i]表示合法且集合中最后一个子串为s[j..i]的集合数目,sum1[i]为dp[1..i]得前缀和,sum2[i]为sum1[1..i]得前缀和。不难发现:如果flag[i]为0,则dp[i]=dp[i-1]。如果flag[i]为1,则dp[i]=i-m+1+sum2[i-m]。证明:若flag[i]=1,说明s[i-m+1...i]=t[1..m],那么s[j..i](j=1..i-m+1)都含有子串t,如果k=1,也就是集合中只有一个串的话,一共有i-m+1种,如果k>=2,那么一个串我们选定s[j..i],能和他匹配的串,因为不能重叠,所以是dp[1]+dp[2]+..+dp[j-1]即sum1[j-1],那么所有的可能就是sum1[1]+sum1[2]...+sum1[i-m],即sum2[i-m]。所以dp[i]=i-m+1+sum2[i-m]

#include <cstdio>
#include <cstring>
#define ll long long
#define N 100100
#define mod 1000000007
int n,m,fail[N],dp[N],sum1[N],sum2[N];
bool flag[N];
char s[N],t[N];
inline void getfail(){
	int k=0;fail[1]=0;
	for(int i=2;i<=m;++i){
		while(k&&t[k+1]!=t[i]) k=fail[k];
		if(t[k+1]==t[i]) ++k;
		fail[i]=k;
	}
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%s%s",s+1,t+1);
	n=strlen(s+1);m=strlen(t+1);
	getfail();int k=0;
	for(int i=1;i<=n;++i){
		while(k&&t[k+1]!=s[i]) k=fail[k];
		if(t[k+1]==s[i]) ++k;
		if(k==m) flag[i]=1;
	}
	for(int i=1;i<=n;++i){
		if(flag[i]){
			dp[i]=(i-m+1+sum2[i-m])%mod;
		}
		else dp[i]=dp[i-1];
		sum1[i]=(sum1[i-1]+dp[i])%mod;
		sum2[i]=(sum2[i-1]+sum1[i])%mod;
	}
	int ans=0;
	for(int i=1;i<=n;++i) ans=(ans+dp[i])%mod;
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值