最长公共子序列 LCS
题目意思:
两个单词长度分别是n, m, 求出最长公共子序列长度 lcs, 然后将连个单词合并,合并后单词总长度等于 n + m - lcs ,
使得两个单词所有的字母都按顺序出现,输出 合并后的单词。
本题要点:
1、套用 LCS 的模板,算出最长公共子序列。 然后,找出这个子序列在 s1 和 s2 中各个字母的下标。
这个需要不断的回溯寻找, 找到 s1[i] == s2[j] 的字母,然后存到数组 d1 和 d2 。
d1 数组表示 最长公共子序列 在 s1 的各个下标
2、输出合并后的单词:(假设最长公共子序列长度 len)
看做是 len 个字母把原来的单词劈开成 len + 1 部分, 假如编号是 1, 2,…, len, len + 1.
输出顺序是 ,先输出 s1 的第1 部分, 再输出s2 的第1 部分, 输出第1 个 公共字母;
输出 s1 的第2 部分, 再输出s2 的第2 部分, 输出第2 个 公共字母
。。。
依次类推。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 110;
char s1[MaxN], s2[MaxN];
int f[MaxN][MaxN]; // f[i][j] 表示 s1的前 i位, s2 的前 j位
int d1[MaxN], d2[MaxN], cnt; // d1 数组表示 最长公共子序列 在 s1 的各个下标
void solve()
{
cnt = 0;
memset(f, 0, sizeof f);
int n = strlen(s1 + 1), m = strlen(s2 + 1);
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(s1[i] == s2[j])
{
f[i][j] = f[i - 1][j - 1] + 1;
}else{
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
}
}
int i = n, j = m;
while(i >= 1 && j >= 1)
{
if(s1[i] == s2[j])
{
d1[++cnt] = i, d2[cnt] = j;
--i, --j;
}else{
if(f[i][j - 1] > f[i - 1][j])
{
--j;
}else{
--i;
}
}
}
int pre1 = 1, pre2 = 1;
for(int i = cnt; i >= 1; --i)
{
int x = d1[i], y = d2[i];
for(int j = pre1; j < x; ++j)
{
printf("%c", s1[j]);
}
for(int j = pre2; j < y; ++j)
{
printf("%c", s2[j]);
}
printf("%c", s1[x]);
pre1 = x + 1, pre2 = y + 1;
}
for(int i = pre1; i <= n; ++i)
{
printf("%c", s1[i]);
}
for(int i = pre2; i <= m; ++i)
{
printf("%c", s2[i]);
}
printf("\n");
}
int main()
{
while(scanf("%s%s", s1 + 1, s2 + 1) != EOF)
{
solve();
}
return 0;
}
/*
apple peach
ananas banana
pear peach
*/
/*
appleach
bananas
pearch
*/