题目描述
有两个仅包含小写英文字母的字符串A和B。
现在要从字符串A中取出k个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?
注意:子串取出的位置不同也认为是不同的方案。
输入格式
第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k,每两个整数之间用一个空格隔开。
第二行包含一个长度为 n 的字符串,表示字符串A。
第三行包含一个长度为 m 的字符串,表示字符串B。
输出格式
输出共一行,包含一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。
数据范围
$ 1 le n le 1000, \\
1 le m le 200,\\
1 le k le m\\ $
$Solution:$
只要看出来是dp就很好做了。
设$dp[i][j][k][0/1]$表示匹配到a的第i位,b的前j位已完成匹配,用了k个子串,a的当前位选没选的方案数。
首先考虑这位不选的情况,显然有$dp[i][j][k][0]=dp[i-1][j][k][0]+dp[i-1][j][k][1]$。上一位选不选皆可。
如果要选这一位,首先必须保证$a_i=b_j$,那么你既可以在上一位也选的情况下沿用之前的那个串,又可以新开一个串,而新开一个串和上一位选不选没什么关系,所以三者相加。
滚动数组。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const ll mod=1e9+7; const int N=1005; int n,m,K; char a[N],b[N]; ll dp[2][202][202][2]; int main() { scanf("%d%d%d",&n,&m,&K); scanf("%s",a+1);scanf("%s",b+1); dp[0][0][0][0]=dp[1][0][0][0]=1; int now=0,pre=1; for(int i=1;i<=n;i++) { now^=1,pre^=1; //memset(dp[now],0,sizeof(dp[now])); for(int j=1;j<=m;j++) { for(int k=1;k<=K;k++) { if(a[i]==b[j]) dp[now][j][k][1]=(dp[pre][j-1][k-1][0]+dp[pre][j-1][k][1]+dp[pre][j-1][k-1][1])%mod; else dp[now][j][k][1]=0; dp[now][j][k][0]=(dp[pre][j][k][0]+dp[pre][j][k][1])%mod; } } } ll ans=(dp[now][m][K][0]+dp[now][m][K][1])%mod; cout<<ans<<endl; return 0; }