1. 问题定义:
给定两个序列 X = {x1, x2, ……, xm } 和 Y = {y1, y2, ……, yn },找出 X 和 Y 的最长公共子序列。
一个给定序列的子序列是在该序列中删去若干个元素后得到的序列。给定两个序列 X 和 Y ,当另一序列 Z 既是 X 的子序列又是 Y 的子序列时,称 Z 是序列 X 和 Y 的公共子序列。
例如,若 X = {A, B, C, B, D, A, B },
Y = {B, D, C, A, B, A },
序列{B, C, A }是 X 和 Y 的一个公共子序列,序列{B, C, B, A }也是 X 和 Y 的一个公共子序列,且为最长公共子序列。
需要注意的是:最长公共子序列与最长公共子串之间的区别。
最长公共子串在原序列中是连续,而最长公共子序列中的各个元素在原序列中不需要连续。如字符串abdewefd和aeffdewsd最长公共子串为dew,而最长公共子序列为adewd。
2. 动态规划求解LCS问题
分析最长公共子序列的定义,从《算法导论》上有
定理:LCS的最优子结构性质
设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:
若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
若xm≠yn且zk≠xm,则Z是Xm-1和Y的最长公共子序列;
若xm≠yn且zk≠yn,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
由最长公共子序列问题的最优子结构性质可知,要找出X=, x<x12, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列,可按以下方式递归地进行:
当xm=yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm(=yn)即可得X和Y的一个最长公共子序列。
当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。
由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。
为了求解LCS问题,需要建立子问题最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。
当i=0或j=0时,空序列是Xi和Yj的最长公共子序列,故c[i,j]=0。
其他情况下,由定理可建立递归关系如下:
3. 代码实现
#include <iostream>
#include <string>
#include <stdlib.h>
typedef enum _eDir
{
LEFT_UP = 0,
UP,
LEFT,
}eDir;
void PrintLCS(const char* pStr, int length1, int length2, int** b)
{
if(pStr == NULL || b == NULL || length1 <=0 || length2 <= 0)
return;
int i=length1, j=length2;
if(b[i][j] == LEFT_UP)
{
PrintLCS(b, pStr, i-1, j-1);
printf(“%c “, pStr[i-1]);
}
else if(b[i][j] == UP)
PrintLCS(b, pStr, i-1, j);
else
PrintLCS(b, pStr, i, j-1);
}
void LCS_Length(const char* pStr1, const char* pStr2)
{
if (pStr1 == NULL || pStr2 == NULL)
throw new std::exception(“Invalid Parameter!”);
int str1Length = strlen(pStr1);
int str2Length = strlen(pStr2);
if(str1Length == 0 || str2Length == 0)
return;
int** c = (int**)new int[str1Length+1];
int** b = (int**)new int[str1Length+1];
for (int i=0; i<str1Length+1; ++i)
{
c[i] = new int[str2Length+1];
b[i] = new int[str2Length+1];
memset(c[i], 0, str2Length+1);
memset(b[i], 0, str2Length+1);
}
for (int i=1; i<=str1Length; ++i)
for (int j=1; j<=str2Length; ++j)
{
if (pStr1[i-1] == pStr2[j-1])
{
c[i][j] = c[i-1][j-1] + 1;
b[i][j] = LEFT_UP;
}
else if(c[i-1][j] >= c[i][j-1])
{
c[i][j] = c[i-1][j];
b[i][j] = UP;
}
else
{
c[i][j] = c[i][j-1];
b[i][j] = LEFT;
}
}
std::cout<<“str1:”<<pStr1<<std::endl<<“str2:”<<pStr2<<std::endl;
PrintLCS(pStr1, str1Length, str2Length, b);
std::cout<<std::endl;
for(int i=0; i<str1Length+1; ++i)
{
delete[] c[i];
delete[] b[i];
}
delete[] c;
delete[] b;
}
int main()
{
char* pStr1 = “ABCBDAB”;
char* pStr2 = “BDCABAB”;
LCS_Length(pStr1, pStr2);
return 0;
}
程序输出:
str1:ABCBDAB
str2:BDCABAB
B C B A B