洛谷 P2364 胖男孩的题解

题目大意

3 3 3 个序列的最长公共子序列(LCS)

思路

我们求 2 2 2 个序列的最长公共子序列是十分简单的,即设 d p i , j dp_{i,j} dpi,j 为第一个字符串前 i i i 位字符串与第二个字符串前 j j j 位字符串的 LCS。

可是这一题有 3 3 3 个序列,怎么办呢?

我们是否可以举一反三

答案是可以的,即设 d p i , j , k dp_{i,j,k} dpi,j,k 为三个字符串前 i , j , k i,j,k i,j,k 位的 LCS 的长度

dp 数组的含义搞清楚了,下面是状态转移:

首先, d p i , j , k = max ⁡ ( d p i − 1 , j , k , d p i , j − 1 , k , d p i , j , k − 1 ) dp_{i,j,k} = \max(dp_{i-1,j,k},dp_{i,j-1,k},dp_{i,j,k-1}) dpi,j,k=max(dpi1,j,k,dpi,j1,k,dpi,j,k1)

然后,如果 a i = b j = c k a_i = b_j = c_k ai=bj=ck,那么就 d p i , j , k = max ⁡ ( d p i , j , k , d p i − 1 , j − 1 , k − 1 + 1 ) dp_{i,j,k} = \max(dp_{i,j,k},dp_{i-1,j-1,k-1}+1) dpi,j,k=max(dpi,j,k,dpi1,j1,k1+1)

是不是很简单?

代码奉上 (你交交试试看)

#include <bits/stdc++.h>
using namespace std;
char a[107], b[107], c[107];
int la, lb, lc;
int dp[107][107][107];
int main() {
	cin >> (a + 1) >> (b + 1) >> (c + 1);
	//这里用到了一个小技巧,把下标变成 1 
	la = strlen(a + 1), lb = strlen(b + 1), lc = strlen(c + 1);
	for(int i = 1; i <= la; i++)
		for(int j = 1; j <= lb; j++)
			for(int k = 1; k <= lc; k++) {
				dp[i][j][k] = max(dp[i - 1][j][k], max(dp[i][j - 1][k], dp[i][j][k - 1]));
				if(a[i] == b[j] && b[j] == c[k])
					dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - 1][k - 1] + 1);
			}
	cout << dp[la][lb][lc];
	return 0;
}

但是本题要求输出的是最长公共子序列,而不是它的长度!一开始我就差点忘了这个,也提醒一下你们,注意上面的代码求的是长度,而不是字符串!

问题来了,怎样输出最长公共子序列?

那么我们可以建个 a n s i , j , k ans_{i,j,k} ansi,j,k,在 dp 过程中记录答案,一切都好办了。

最后输出 a n s l a , l b , l c ans_{la, lb, lc} ansla,lb,lc

时间复杂度: O ( n 3 ) O(n^3) O(n3);空间复杂度: O ( n 3 ) O(n^3) O(n3)。绰绰有余。

代码如下:

#include <bits/stdc++.h>
using namespace std;
char a[107], b[107], c[107];
int la, lb, lc;
int dp[107][107][107];
string ans[107][107][107];
int main() {
	cin >> (a + 1) >> (b + 1) >> (c + 1);
	//这里用到了一个小技巧,把下标变成 1 
	la = strlen(a + 1), lb = strlen(b + 1), lc = strlen(c + 1);
	for(int i = 1; i <= la; i++)
		for(int j = 1; j <= lb; j++)
			for(int k = 1; k <= lc; k++) {
				if(a[i] == b[j] && b[j] == c[k])
					if(dp[i - 1][j - 1][k - 1] + 1 > dp[i][j][k]) {
						dp[i][j][k] = dp[i - 1][j - 1][k - 1] + 1;
						ans[i][j][k] = ans[i - 1][j - 1][k - 1] + a[i];
					}
				if(dp[i - 1][j][k] > dp[i][j][k]) { //分情况讨论,水题一道 
					dp[i][j][k] = dp[i - 1][j][k];
					ans[i][j][k] = ans[i - 1][j][k];
				}
				if(dp[i][j - 1][k] > dp[i][j][k]) {
					dp[i][j][k] = dp[i][j - 1][k];
					ans[i][j][k] = ans[i][j - 1][k];
				}
				if(dp[i][j][k - 1] > dp[i][j][k]) {
					dp[i][j][k] = dp[i][j][k - 1];
					ans[i][j][k] = ans[i][j][k - 1];
				}
			}
	cout << ans[la][lb][lc];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值