【题解&&海亮集训&&dp】子串

41 篇文章 0 订阅
24 篇文章 1 订阅
题目传送门

题目描述:
有两个仅包含小写英文字母的字符串 AA 和 BB。现在要从字符串 AA 中取出 kk 个互不重叠的非空子串,然后把这 kk 个子串按照其在字符串 AA 中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串 BB 相等?注意:子串取出的位置不同也认为是不同的方案。


【输入格式】
输入文件名为 substring.in
第一行是三个正整数 nn,mm,kk,分别表示字符串 AA 的长度,字符串 BB 的长度,以及【问题描述】中所提到的 kk,每两个整数之间用一个空格隔开。
第二行包含一个长度为 nn 的字符串,表示字符串 AA。
第三行包含一个长度为 mm 的字符串,表示字符串 BB。


【输出格式】
输出文件名为 substring.out。
输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。


【样例 1】
6 3 1
aabaab
aab

2


【样例 2】
6 3 2
aabaab
aab

7


【样例 3】
6 3 3
aabaab
aab

7


样例解释如下:


分析:大致看一眼题目跟最长公共序列差不多,只不过题目因为提到了子序列分的段数,所以我们只需要多加一维k,设dp1[i][j][k]表示A串的前i 个字符分成了k个不重叠的子串连接起来后形成了B串的前j个字符。但是我们仔细一想发现不好转移,就可以再加一个数组在第一个数组dp2[i][j][k]的基础上表示最后一个字符是第i个字符,我们不难得到以下方程:
d p 2 [ i ] [ j ] [ k ] = { d p 1 [ i − 1 ] [ j − 1 ] [ k − 1 ] + d p 2 [ i − 1 ] [ j − 1 ] [ k ] , s1[i]==s2[j] 0 s1[i]!=s2[j] dp2[i][j][k] = \begin{cases} dp1[i-1][j-1][k-1]+dp2[i-1][j-1][k], & \text{s1[i]==s2[j]} \\[2ex] 0 & \text{s1[i]!=s2[j]} \\[2ex] \end{cases} dp2[i][j][k]=dp1[i1][j1][k1]+dp2[i1][j1][k],0s1[i]==s2[j]s1[i]!=s2[j]

其中第一个是说明以i结尾的dp2[i][j][k]可以由当前的s1[i]新开一个段以及不新开一段的个数,第二个是说不相等,那么就没有方案

dp1可以这样转移:

d p 1 [ i ] [ j ] [ k ] = d p 1 [ i − 1 ] [ j ] [ k ] + d p 2 [ i ] [ j ] [ k ] ; dp1[i][j][k]=dp1[i-1][j][k]+dp2[i][j][k]; dp1[i][j][k]=dp1[i1][j][k]+dp2[i][j][k];

又因为这个题会卡空间,所以最后只要用滚动数组搞一搞就行。

那么具体代码如下:

#include<bits/stdc++.h>
using namespace std;
#define P 1000000007
int len1,len2,k;
char s1[100001],s2[100001];
int dp1[2][2001][2001];
int dp2[2][2001][2001];
int main(){
	scanf("%d %d %d",&len1,&len2,&k);
    for (int i=1;i<=len1;i++) {s1[i]=getchar();while (s1[i]<'a'||s1[i]>'z') s1[i]=getchar();}
    for (int i=1;i<=len2;i++) {s2[i]=getchar();while (s2[i]<'a'||s2[i]>'z') s2[i]=getchar();}
//    for (int i=1;i<=len1;i++) cout<<s1[i];
//    cout<<endl;
//    for (int i=1;i<=len2;i++) cout<<s2[i];
//    cout<<endl;
    dp1[0][0][0]=dp1[1][0][0]=1;
    for (int i=1;i<=len1;i++)
      for (int j=1;j<=len2;j++)
        for (int p=1;p<=k;p++){
		    if (s1[i]!=s2[j]) dp2[i&1][j][p]=0;
		    else dp2[i&1][j][p]=(dp1[i-1&1][j-1][p-1]+dp2[i-1&1][j-1][p])%P;
		    dp1[i&1][j][p]=(dp1[i-1&1][j][p]+dp2[i&1][j][p])%P;
		}
//	for (int i=1;i<=len1;i++){
//	    for (int j=1;j<=len2;j++) cout<<dp1[i&1][j][k]<<' ';
//	    cout<<endl;
//	}
	printf("%d",dp1[len1&1][len2][k]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值