题目大意
求 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(dpi−1,j,k,dpi,j−1,k,dpi,j,k−1)。
然后,如果 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,dpi−1,j−1,k−1+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;
}