LOJ2424 NOIP2015 子串 【DP】*

本文深入解析了LOJ2424 NOIP2015子串问题,采用动态规划方法求解从序列A中选取K段不重叠子串组成序列B的方案数量。通过优化DP状态表示和转移方程,实现了高效求解。

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

LOJ2424 NOIP2015 子串


LINK


题目大意是给你两个序列,在a序列中选出k段不重叠的子串组成b序列,问方案数

首先我们不考虑相邻的两段,把所有相邻段当成一段进行计算

然后设 d p i , j , k , 0 / 1 dp_{i,j,k,0/1} dpi,j,k,0/1表示a使用了i为,b匹配到j位,一共有k段,当前这一位选不选的方案数

然后转移显然:
d p i , j , k , 0 = d p i − 1 , j , k , 0 + d p i − 1 , j , k , 1 dp_{i,j,k,0}=dp_{i−1,j,k,0}+dp_{i−1,j,k,1} dpi,j,k,0=dpi1,j,k,0+dpi1,j,k,1
d p i , j , k , 1 = d p i − 1 , j − 1 , k , 1 + d p i − 1 , j − 1 , k − 1 , 0 dp_{i,j,k,1}=dp_{i−1,j−1,k,1}+dp_{i−1,j−1,k−1,0} dpi,j,k,1=dpi1,j1,k,1+dpi1,j1,k1,0 (条件 a i = b j a_i=b_j ai=bj)
然后之后的dp就非常显然了

把i滚动数组掉就可以了

然后注意数组清零和j和k的枚举下界是0。。。

#include<bits/stdc++.h>
using namespace std;
const int Mod=1e9+7;
const int N=1e3+10,M=2e2+10;
#define fu(a,b,c) for(int a=b;a<=c;a++)
int dp[2][M][M][2];
int c[N][N];
int n,m,k,ind=0;
char a[N],b[N];
int add(int a,int b){return (a+b)%Mod;}
int mul(int a,int b){return 1ll*a*b%Mod;}
void init() {
  fu(i,0,n)c[i][0]=1;
  fu(i,1,n)fu(j,1,i)c[i][j]=add(c[i-1][j],c[i-1][j-1]);
}
int main() {
  scanf("%d%d%d",&n,&m,&k);
  scanf("%s%s",a+1,b+1);
  init();
  dp[ind][0][0][0]=1;
  fu(i,1,n) {
    ind^=1;
    memset(dp[ind],0,sizeof(dp[ind]));
    fu(j,0,min(i,m))
      fu(k,0,j) {
        dp[ind][j][k][0]=add(dp[ind^1][j][k][0],dp[ind^1][j][k][1]);
        if(k==0||j==0||a[i]!=b[j])continue;
        dp[ind][j][k][1]=add(dp[ind^1][j-1][k][1],dp[ind^1][j-1][k-1][0]);
      }
  }
  int ans=0;
  fu(i,1,k)ans=add(ans,mul(add(dp[ind][m][i][0],dp[ind][m][i][1]),c[m-i][k-i]));
  printf("%d",ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值