P2679 子串

P2679 子串

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

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

注意:子串取出的位置不同也认为是不同的方案。

答案模1000000007

Solution

(dp_{i, j, k, 1/0}) 表示 考虑 (A)(i) 个, (B)(j) 个, 在 (A) 中选取 (k) 个子串, 第 (i) 位 选 / 不选进子串 的方案数 转移时, 考虑前一种状态, 判断新加入的字符作为新子串还是老子串即可 $$dp_{i, j, k, 0} = dp_{i - 1, j, k, 0} + dp_{i - 1, j, k, 1}$$$$dp_{i, j, k, 1} = dp_{i - 1, j - 1, k - 1, 0} + dp_{i - 1, j - 1, k - 1, 1} + dp_{i - 1, j - 1, k, 1} [if(A_ == B_)]$$$$dp_{i, j, k, 0} = 0 [Else]$$ 不选直接继承上一步答案即可 选的话为以下三种情况的和: 前两项是作为新子串, 第三项是作为后缀接在老的子串后面的方案数

但是这样转移的话空间无法承受, 观察转移只与 (i - 1) 有关, 滚掉 (i) 维即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 2019, M = 1000000007;
int lenA, lenB, K;
char A[maxn], B[maxn];
int dp[2][419][419][2];//匹配到B的前i个,A中选k段,当前i选或不选的方案数
int main(){
	lenA = RD(), lenB = RD(), K = RD();
	scanf("%s%s", A + 1, B + 1);
	dp[0][0][0][0] = dp[1][0][0][0] = 1;//滚动刚开始为1,对应起点为0
	REP(i, 1, lenA){
		int now = i & 1, pre = now ^ 1;
		REP(j, 1, lenB){
			REP(k, 1, K){
				dp[now][j][k][0] = ((dp[pre][j][k][0] + dp[pre][j][k][1]) % M + M) % M;//不选
				if(A[i] == B[j]){
					dp[now][j][k][1] = //选
					((dp[pre][j - 1][k - 1][0] + 
					dp[pre][j - 1][k - 1][1]) % M + M) % M + //前面这两种情况都可以开新子串
					dp[pre][j - 1][k][1];//或者接在之前的串后面
					dp[now][j][k][1] = (dp[now][j][k][1] % M + M) % M;
					}
				else dp[now][j][k][1] = 0;
				}
			}
		}
	printf("%d
", ((dp[lenA & 1][lenB][K][0] + dp[lenA & 1][lenB][K][1]) % M + M) % M);
	return 0;
	}
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9875838.html