7-27 两个字符串的所有最长公共子序列
分数 15
求两个字符串的所有最长公共子序列。
输入格式:
输入长度≤100的两个字符串。
输出格式:
输出两个字符串的所有最长公共子序列,若最长公共子序列多于1个,则将所有子序列按字典序从小到大排序后输出。
输入样例1:
ABCBDAB
BDCABA
输出样例1:
BCAB
BCBA
BDAB
输入样例2:
ABACDEF
PGHIK
输出样例2:
NO
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
思路
设\(dp[i][j]\)表示对于字符串\(a\)的前i个字符和字符串\(b\) 的前j个字符,构成最长公共子序列的长度
\(\begin{equation} dp[i][j]=\left\{ \begin{aligned} max(dp[i - 1][j],dp[i][j - 1])\quad a[i] != a[j]\\ dp[i - 1][j - 1] + 1\quad a[i] = a[j]\\ \end{aligned} \right . \end{equation}\)
回溯
我们从\(dp[n][m]\)开始考虑,若当前第\(n\)位和第\(m\)位相等,那么这一位一定是LCS的一位,我们把状态递推到\(n - 1, m - 1\), 若这两位不相等,则比较\(dp[n - 1][m]\)和\(dp[n][m - 1]\)的大小,若不相等选择序列较长的进行递推,若相等则将问题划分为两个问题,递归求解。
Code
#include <iostream>
#include <cstring>
#include <vector>
#include <set>
using namespace std;
/*
dp[i][j] 表示 a串长度为i b串长度为j的最长公共子序列
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] + 1);
aabcc
abcc
*/
char a[110], b[110];
int dp[110][110], n, m;
set<string> s1;
void go_back(int i, int j, string str) {
while(i >= 1 && j >= 1) {
if(a[i] == b[j]) {
str = a[i] + str;
i --, j --;
} else {
if(dp[i - 1][j] > dp[i][j - 1]) {
i --;
} else if(dp[i - 1][j] < dp[i][j - 1]) {
j --;
} else{
go_back(i - 1, j, str);
go_back(i, j - 1, str);
return;
}
}
}
if(str.length()) s1.insert(str);
}
int main() {
cin >> (a+1) >> (b+1);
n = strlen(a+1), m = strlen(b+1);
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
if(a[i]==b[j]) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
cout << dp[i][j] << " \n"[j == m];
}
}
go_back(n, m, "");
for (auto &x: s1) {
cout << x << "\n";
}
}