Codeforces 494 B Obsessive String题解(KMP+DP)

本文详细解析了CF494B题目的解题思路,利用KMP算法结合DP方法,通过寻找字符串S中包含字符串T的所有可能子串位置,进而计算出满足条件的不同方案数量。文章提供了完整的代码实现,包括KMP匹配、状态转移方程和最终答案的计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:CF 494 B.
题目大意:给定两个字符串 S S S T T T,在 S S S中抽取几个不相交的子串,使得 T T T均是这些子串的子串,问有多少种方案.
1 ≤ ∣ S ∣ , ∣ T ∣ ≤ 1 0 5 1\leq |S|,|T|\leq 10^5 1S,T105.

仔细一看感觉题目好像比较难,但看完题解后感觉这也是一道比较容易的套路题,KMP与DP结合的题做的少啊…

首先很容易想到第一个串的子串必须包含第二个串,就很自然想到用KMP将第二个串在第一个串中出现的位置都找出来,那么所有的子串都必须包含这些位置.

考虑计算出一个数组 v a l [ i ] val[i] val[i]表示第 i i i个字符前最近的匹配点(第 i i i个点不一定要匹配),发现这个数组可以很容易用KMP求出匹配点后稍微处理一下求出.

考虑应用 v a l val val数组,设 f [ i ] f[i] f[i]表示到前 i i i个字符的方案数(所选子串中不一定要包含第 i i i个字符),然后很容易发现这个状态的转移有两种情况:
1.去掉第 i i i个字符,也就是 f [ i − 1 ] f[i-1] f[i1].
2.找到最近的匹配点 v a l [ i ] val[i] val[i],考虑 f [ 1.. v a l [ i ] − 1 ] f[1..val[i]-1] f[1..val[i]1],发现可以只选串 [ v a l [ i ] . . i ] [val[i]..i] [val[i]..i]或者在 f [ 1.. v a l [ i ] − 1 ] f[1..val[i]-1] f[1..val[i]1]后面直接加入这个串.

所以转移就是:
f [ i ] = f [ i − 1 ] + v a l [ i ] + ∑ j = 1 v a l [ i ] − 1 f [ j ] f[i]=f[i-1]+val[i]+\sum_{j=1}^{val[i]-1}f[j] f[i]=f[i1]+val[i]+j=1val[i]1f[j]

转移出来之后,发现答案就是 f [ n ] f[n] f[n]了,所以直接输出就好.

代码如下:

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

#define Abigail inline void
typedef long long LL;

const int N=100000;
const LL mod=1000000007;

char s[N+9],t[N+9];
int n,m,nxt[N+9];
LL f[N+9],sum[N+9],val[N+9];

void add(LL &a,LL b){a+=b;while (a>=mod) a-=mod;}

void self_mate(){
  int j=0;
  nxt[1]=0;
  for (int i=2;i<=m;++i){
    while (t[i]^t[j+1]&&j>0) j=nxt[j];
    if (t[i]==t[j+1]) ++j;
    nxt[i]=j;
  }
}

void mate(){
  int j=0;
  for (int i=1;i<=n;++i){
    while (s[i]^t[j+1]&&j>0) j=nxt[j];
    if (s[i]==t[j+1]) ++j;
    if (j==m){
      val[i]=i-m+1;
      j=nxt[j];
	}
  }
}

void solve(){
  for (int i=1;i<=n;++i)
    if (val[i]==0) val[i]=val[i-1];
  for (int i=1;i<=n;++i){
  	f[i]=f[i-1];
  	if (val[i]) add(f[i],sum[val[i]-1]+val[i]);
  	add(sum[i],sum[i-1]+f[i]);
  }
}

Abigail into(){
  scanf("%s",s+1);
  scanf("%s",t+1);
  n=strlen(s+1);
  m=strlen(t+1);
}

Abigail work(){
  self_mate();
  mate();
  solve();
}

Abigail outo(){
  printf("%I64d\n",f[n]);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值