Loading

题解 lg2679 子串 --线性dp

题意

有两个仅包含小写英文字母的字符串A 和B。

现在要从字符串 A 中取出 k个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B相等

思路

我最开始并没有想到DP

最开始以为是一道字符串的题目,不过问题方案与数据范围提示我们这道题其实可以DP

\(f_{i,j,k}\)表示A以\(i\)为结尾,B以第\(j\)位为结尾,一共取了\(k\)个子串的方案数

那么可以得到以下DP式子

\[f_{i,j,k}=\begin{cases} f_{i-1,j,k},A_i\neq B_j \\f_{i-1,j,k}+f_{i-1,j-1,k-1},A_i= B_j\\f_{i-1,j,k}+f_{i-1,j-1,k-1}+f_{i-2,j-2,k-1},A_i=B_j,A_{i-1}=B_{j-1}\\...\end{cases} \]

可以发现DP式只与\(i,i-1\)有关,就可以用类似01背包滚动数组的方法来优化空间

另外,我们会发现如果有相等的字符,那么求值会新添一个循环,我们可以考虑记录相等字符带来的贡献,即用\(sum_{j,k}\)表示在B以第\(j\)位为结尾,一共取了\(k\)个子串时的第j位与其之前连续的相等字符的贡献.

\[sum_{j,k}=\begin{cases}0,A_i\neq B_j \\sum_{j-1,k}+f_{j-1,k-1},A_i=B_j\end{cases} \]

其实就是对\(f_{i-1,j-1,k-1}+f_{i-2,j-2,k-1}+...\)的求和

这样就好了

时间复杂度\(O(nmk)\),空间复杂度\(O(mk)\)

代码

#include<bits/stdc++.h>
using namespace std;
int const MOD=1e9+7;
int n,m,nk;
char A[1010],B[1010];
int f[210][210]={1},sum[210][210];
int main(){
	scanf("%d%d%d%s%s",&n,&m,&nk,A+1,B+1);
	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
			for(int k=nk;k>=1;k--){
				f[j][k]=(f[j][k]+(sum[j][k]=(A[i]==B[j]?sum[j-1][k]+f[j-1][k-1]:0)%MOD))%MOD;
			}
		}
	}
	printf("%d\n",f[m][nk]);
	return 0;
}
posted @ 2020-11-09 17:01  fpjo  阅读(89)  评论(0编辑  收藏  举报