题目传送门
题目描述:
有两个仅包含小写英文字母的字符串 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[i−1][j−1][k−1]+dp2[i−1][j−1][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[i−1][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;
}