CF346B kmp+dp

这道题应该是比较典型的利用kmp去dp的题目。
我们思考题意他要求我们的子序列不能够和virus串完全匹配,那么我们就可以在原先求最长公共子序列的基础上在加一个维度。就是
f[i][j][z],第一个串枚举到i,第二个串枚举到j,然后他们与virus串匹配的长度是z。
接下来思考转移。
第一个串当做A数组,第二个串当做B数组
即当(A[i] == B[j])的时候可以在原先已经有的子序列基础上加上字符A[i],这个时候与virus匹配到的长度是z。那么这个长度z是由谁转移来的?
这个地方需要kmp去预处理。我们枚举virus串的长度,然后就假设当前长度为templen,然后枚举下一个字符是谁‘A’-‘Z’,利用next数组就可以得到加上下一个字符后,与virus串匹配上的长度,(这个部分就是kmp的最基础应用)
预处理后我们就可以知道有哪些状态加上A【i】这个字符可以到达z状态,这就可以进行转移了。

唯一与最长公共子序列有区别就是(A[i] == B[j]),其他是一样的。
复杂度就是O( | s1 | * | s2 | * | virus|)
在输出路径方面就是和大部分输出路径一样倒推回去就好了

char A[max_], B[max_], S[max_];
int next_[max_],lenS,lenA,lenB;
il void getnext() {
	lenS = strlen(S + 1);
	int j = 0;
	for (int i = 2; i <= lenS; i++) {
		while (j&&S[j + 1] != S[i]){
			j = next_[j];
		}
		if (S[j + 1] == S[i])j++;
		next_[i] = j;
	}
}
vector<int> to[max_][30];
int f[max_][max_][max_];
void dfs(int i, int j, int z) {
	if (A[i] == B[j]) {
		for (auto qian : to[z][A[i] - 'A']) {
			if (f[i][j][z] == f[i - 1][j - 1][qian] + 1) {
				dfs(i - 1, j - 1, qian);
				cout << A[i];
				return;
			}
		}
	}
	if (f[i][j][z] == f[i - 1][j][z]) {
		dfs(i - 1, j, z); return;
	}
	if (f[i][j][z] == f[i][j - 1][z]) {
		dfs(i, j - 1, z); return;
	}
}
signed main() {	
	cin >> (A + 1) >> (B + 1) >> (S + 1);
	lenA = strlen(A + 1);
	lenB = strlen(B + 1);
	getnext();
	for (int i = 0; i <= lenS; i++) {
		for (int j = 0; j <= 25; j++) {
			int now = i;
			while (now && S[now + 1]!='A'+j){
				now = next_[now];
			}
			if (S[now + 1] == 'A' + j)now++;
			to[now][j].push_back(i);
		}
	}
	for (int i = 1; i <= lenA; i++) {
		for (int j = 1; j <= lenB; j++) {
			for (int z = 0; z < lenS; z++) {
				if (A[i] == B[j]) {
					for (auto qian : to[z][A[i] - 'A']) {
						f[i][j][z] = max(f[i][j][z], f[i - 1][j - 1][qian] + 1);
					}
				}
				f[i][j][z] = max(f[i][j][z], f[i - 1][j][z]);
				f[i][j][z] = max(f[i][j][z], f[i][j - 1][z]);
			}
		}
	}
	int ans = 0,stu;
	for (int i = 0; i < lenS; i++) {
		if (ans < f[lenA][lenB][i]) {
			ans = f[lenA][lenB][i];
			stu = i;
		}
	}
	if (ans == 0) { cout << 0; return 0; }
	dfs(lenA, lenB, stu);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值