洛谷 P2679 子串
题目链接
题目描述
有两个仅包含小写英文字母的字符串A和B。
现在要从字符串A中取出k个互不重叠的非空子串,然后把这k个子串按照其在字符串A中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串B相等?
注意:子串取出的位置不同也认为是不同的方案。
题目分析
这道题是一道比较明显的动态规划。我们先考虑划分状态。由于要从字符串A中取出k个互不重叠的非空子串,使它们连接起来与B相等,我们容易想到记录在字符串A中已处理的长度、在字符串B中已处理的长度和已取出子串的个数。但这样还是难以将状态转移方程推出来。我们考虑再加入一维来表示对于字符串A的当前这一位是否被选择。这样我们就可以开始推导方程式了。如果A的当前这一位不等于B的当前这一位,那么意味着它不可能被选择,所以它未被选择的方案数即为前一位选择或者不选择进当前当前子串两种方案数的总和,而被选择的方案数为零;如果等于,那么意味着它可选可不选,若选择,则其方案数为上一位选择进这个子串、选择进上个子串和不选择的方案数的总和,若不选择则与上一个情况相同。
这样,设fi,j,k,opt表示在字符串A中处理到第i个字符,字符串B中处理到第j个字符,当前选择的是第k个子串,选择当前字符的情况为opt(opt为0指当前字符选入,opt为1指当前字符不选入)的时候的方案总数,则有
fi,j,k,0=∑{fi-1,j,l,1+fi-1,j,l,0},fi,j,k,1=A[i]==B[j]?∑{fi-1,j-1,l,1+fi-1,j-1,l-1,0+fi-1,j-1,l-1,1}:0(1<=l<=k)。
但如果按照上面的方程,数组需要开到200*200*200*2,显然是不可能的。我们注意到,对于第一维,其实只用到了i和i-1两个相邻的状态,于是我们可以运用滚动数组,将第一维所需空间滚动成2,空间就没有问题了。
代码
1 #include<algorithm> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int n,m,k,f[2][201][201][2]; 6 char a[1001],b[201]; 7 const int mod=1000000007; 8 int main() 9 { 10 scanf("%d%d%d%s%s",&n,&m,&k,a+1,b+1); 11 f[1][0][0][0]=f[0][0][0][0]=1; 12 for(int i=1;i<=n;++i) 13 for(int j=1;j<=m;++j) 14 for(int l=1;l<=k;++l) 15 { 16 f[i&1][j][l][0]=(f[i+1&1][j][l][1]+f[i+1&1][j][l][0])%mod; 17 f[i&1][j][l][1]=a[i]==b[j]?((f[i+1&1][j-1][l][1]+f[i+1&1][j-1][l-1][0])%mod+f[i+1&1][j-1][l-1][1])%mod:0; 18 } 19 printf("%d",(f[n&1][m][k][1]+f[n&1][m][k][0])%mod); 20 return 0; 21 }