题目描述
有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出的位置不同也认为是不同的方案。
输入
第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问 题描述中所提到的 k,每两个整数之间用一个空格隔开。第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。
输出
输出共一行,包含一个整数,表示所求方案数。(由于答案可能很大,所以这里要求输 出答案对 1,000,000,007 取模的结果。)
样例输入
6 3 1
aabaab
aab
样例输出
2
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。
题解
好难啊,还好没在15年考。
解1:
设 s[ i ][ j ][ k ] 为A用到了 i ,B用到了 j ,已经用了 k 个子串, 并且一定用了当前字符 ( A[ i ] ) 时的方案数。
设 f[ i ][ j ][ k ] 为A用到了 i ,B用到了 j ,已经用了 k 个子串, 无论用不用当前字符 ( A[ i ] ) 时的方案数总和。
我也不知道为什么这么设
先考虑 s 的转移:
当A[ i ] == B[ i ] 时,可以是A[ i ] 单独一段,也可以是A[ i ] 与前面的共成一段。
A[ i ] 单独一段:s[ i ][ j ][ k ] = f[ i-1 ][ j-1 ][ k-1 ]
A[ i ] 与前面共成一段: s[ i ][ j ][ k ] = s[ i-1 ][ j-1 ][ k ]
所以: s[ i ][ j ][ k ] = f [ i-1 ][ j-1 ][ k-1 ] + s[ i-1 ][ j-1 ][ k ]
当A[ i ] != B[ i ] 时,s[ i ][ j ][ k ] = 0
再考虑 f 的转移:
f 的转移也有两种情况,
选择A[ i ] :f [ i ][ j ][ k ] = s[ i ][ j ][ k ]
不选择A[ i ] :f [ i ][ j ][ k ] = f [ i-1 ][ j ][ k ]
所以: f [ i ][ j ][ k ] = s[ i ][ j ][ k ] + f [ i-1 ][ j ][ k ]
答案为 f [ n ][ m ][ k ] 。
由于数据较大,所以把第一维滚掉。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1000+50; const int maxm=200+50; const int mod=1000000007; int n,m,k,s[2][maxm][maxm],f[2][maxm][maxm],cur; char a[maxn],b[maxm]; template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ read(n),read(m),read(k); cin>>a+1>>b+1; f[0][0][0]=1; for(int i=1;i<=n;i++){ cur^=1; f[cur][0][0]=1; for(int j=1;j<=m;j++) for(int w=1;w<=k;w++){ if(a[i]==b[j]) s[cur][j][w]=(s[cur^1][j-1][w]+f[cur^1][j-1][w-1])%mod; else s[cur][j][w]=0; f[cur][j][w]=(s[cur][j][w]+f[cur^1][j][w])%mod; } } cout<<f[cur][m][k]; return 0; }
解2:
四维dp,dp[ i ][ j ][ k ][ 0/1 ]表示A数组选到了 i ,B数组选到了 j,用了 k 段,选不选A[ i ] 的方案数。和方法1的转移的方式大体一样,初始化dp[ i ][ 0 ][ 0 ][ 0 ] = 1 。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1000+50; const int maxm=200+50; const int mod=1000000007; int n,m,k,dp[2][maxm][maxm][2],cur; char a[maxn],b[maxm]; template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ read(n),read(m),read(k); cin>>a+1>>b+1; dp[0][0][0][0]=1; for(int i=1;i<=n;i++){ cur^=1; dp[cur][0][0][0]=1; for(int j=1;j<=m;j++) for(int t=1;t<=k;t++){ if(a[i]==b[j]) dp[cur][j][t][1]=((dp[cur^1][j-1][t][1]+dp[cur^1][j-1][t-1][1])%mod+dp[cur^1][j-1][t-1][0])%mod; else dp[cur][j][t][1]=0; dp[cur][j][t][0]=(dp[cur^1][j][t][0]+dp[cur^1][j][t][1])%mod; } } cout<<(dp[cur][m][k][1]+dp[cur][m][k][0])%mod<<endl; return 0; }