分析:
-
先求出lcs的长度
-
dfs处理两个子串,找出所有lcs方案并且输出
lcs的求法
最大公共子序列问题_qq12323qweeqwe的博客-CSDN博客、
状态计算:可以分为不重不漏的四个子集
- a[i]在,b[j]在 dp[i-1][j-1]+1
- a[i]在,b[j]不在 dp[i][j-1]
- a[i]不在,b[j]在 dp[i-1][j]
- a[i]不在,b[j]不在 dp[i-1][j-1]
寻找lcs方案:
由于我们已经知道了lcs是多少了,那么寻找lcs方案就方便得多了,只要dfs暴力遍历一遍,然后和状态表示图一样,增加相应的字符就行了
暴力枚举,会T
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
const int N = 105;
char a[N];
char b[N];
int dp[N][N];
int res;
int la, lb;
set<string> s;
void dfs(int i, int j, int now, string str)
{
if (now==res)
{
s.insert(str);
return;
}
if (i > la || j > lb)
return;
if (a[i] == b[j])
dfs(i + 1, j + 1, now + 1, str + a[i]);
dfs(i + 1, j, now, str );
dfs(i, j + 1, now, str );
}
int main()
{
cin >> a + 1;
cin >> b + 1;
la = strlen(a+1);
lb = strlen(b+1);
for (int i = 1;i <= la;i++)
for (int j = 1;j <= lb;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);
}
res = dp[la][lb];
dfs(1, 1, 0, "");
for (auto t : s)
{
cout << t << '\n';
}
return 0;
}
预处理优化
由于使用暴力枚举会tle,我们要寻找一种优化
发现,其实能够影响dfs的形参now和str的只有a[i]==b[j]的情况
那么我们只要保存a[i]==b[j],就可以极大限度的剪枝
由于有26个字母,一个最长公共子序列每个位置上的字母未知,那么就用26个字母枚举一遍各个位置可能的字母,如果这种情况可能就继续dfs,否则就跳过
首先需要对两个字符串进行预处理
- pa[j][i] 字母 char('a'+j) 在字符串1~i中最后出现的位置
- pb[j][i] 字母 char('a'+j) 在字符串1~i中最后出现的位置
for(int i=1;i<=la;i++)
for (int j = 0;j < 26;j++)
{
if (a[i] == 'a' + j) pa[j][i] = i;
else pa[j][i] = pa[j][i - 1];
}
for (int i = 1;i <= lb;i++)
for (int j = 0;j < 26;j++)
{
if (b[i] == 'a' + j) pb[j][i] = i;
else pb[j][i] = pb[j][i - 1];
}
dfs
void dfs(int i, int j, int now, string str)
{
if (!now) //满足条件存入
{
s.insert(str);
return;
}
if (!i || !j) //越界跳过
return;
else
{
for (int k = 0;k < 26;k++)
{ //记录第k个字母在1~i,1~j中最后一次出现的位置
int pi = pa[k][i], pj = pb[k][j];
if (dp[pi][pj] == now) //如果最后一次出现的位置是lcs第now个字母,继续递归
dfs(pi - 1, pj - 1, now - 1, char('a' + k) + str);
//直接跳过pi,pj前面递归
}
}
}
完整ac代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
const int N = 105;
char a[N];
char b[N];
int dp[N][N];
int res;
int la, lb;
int pa[N][N];
int pb[N][N];
set<string> s;
void dfs(int i, int j, int now, string str)
{
if (!now)
{
s.insert(str);
return;
}
if (!i || !j)
return;
else
{
for (int k = 0;k < 26;k++)
{
int pi = pa[k][i], pj = pb[k][j];
if (dp[pi][pj] == now)
dfs(pi - 1, pj - 1, now - 1, char('a' + k) + str);
}
}
}
int main()
{
cin >> a + 1;
cin >> b + 1;
la = strlen(a+1);
lb = strlen(b+1);
for (int i = 1;i <= la;i++)
for (int j = 1;j <= lb;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);
}
res = dp[la][lb];
for(int i=1;i<=la;i++)
for (int j = 0;j < 26;j++)
{
if (a[i] == 'a' + j) pa[j][i] = i;
else pa[j][i] = pa[j][i - 1];
}
for (int i = 1;i <= lb;i++)
for (int j = 0;j < 26;j++)
{
if (b[i] == 'a' + j) pb[j][i] = i;
else pb[j][i] = pb[j][i - 1];
}
dfs(la, lb, res, "");
for (auto t : s)
cout << t << '\n';
return 0;
}