考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,
并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”
和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”
和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”
和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”
和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,
找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”
和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”
和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”
和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”
和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”
和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,
找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”
和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
0, (i==0 or j==0)
dp[i][j] = dp[i-1][j-1], (i,j>0且a[i]==b[j])
max(dp[i-1][j],dp[i][j-1]), (i,j>0且a[i]!=b[j])
dp[i][j] = dp[i-1][j-1], (i,j>0且a[i]==b[j])
max(dp[i-1][j],dp[i][j-1]), (i,j>0且a[i]!=b[j])
dp[i][j]的含义:第一个字符串前i个元素和第二个字符串前j个元素的最长公共子序列的长度
p[i][j]用于记录路径:递归输出最长公共子序列。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1005
using namespace std;
char a[N] = {'*'}, b[N] = {'*'};
int dp[N][N];
int p[N][N];//记录路径
int len1, len2;
void init()
{
for (int i = 0; i <= len1; i++)
dp[i][0] = 0;
for (int i = 0; i <= len2; i++)
dp[0][i] = 0;
}
void LCSlength()
{
for (int i = 1; i <= len1; i++)
{
for (int j = 1; j <= len2; j++)
{
if (a[i] == b[j])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
p[i][j] = 1;
}
else if (dp[i - 1][j] >= dp[i][j - 1])
{
dp[i][j] = dp[i - 1][j];
p[i][j] = 2;
}
else
{
dp[i][j] = dp[i][j - 1];
p[i][j] = 3;
}
}
}
}
void printLCS(int i, int j)
{
if (i == 0 || j == 0) return;
if (p[i][j] == 1)
{
printLCS(i - 1, j - 1);
printf("%c", a[i]);
}
else if (p[i][j] == 2)
printLCS(i - 1, j);
else
printLCS(i, j - 1);
}
int main()
{
while (~scanf("%s %s", a+1, b+1))
{
len1 = strlen(a) - 1, len2 = strlen(b) - 1;
init();
LCSlength();
printf("%d\n", dp[len1][len2]);
printLCS(len1, len2);
printf("\n");
}
return 0;
}