一个给定序列的子序列就是将给定子序列中零个或多个元素去掉之后得到的结果,即子序列要保持与原序列的相对顺序,不是一定要是原序列中连续的一块。正如最长公共子串和最长公共子序列的区别,前者要求是原序列中连续的一段。
给定序列X和Y,求解他们的最长公共子序列。最直接的方法就是暴力搜索的方法,穷举X的所有子序列,对每个子序列检查它是否也是Y的子序列,记录找到的最长子序列。因为X的每个子序列对应X的下标集合{1,2,3...m}的一个子集,所以X有2^m个子序列。所以运行时间是指数阶的,如果原序列较长的话比较不实用。另外一个最经典的方法就是动态规划...
下面说下说下我理解的最长公共子序列求解步骤(来自算法导论)
1.最长公共子序列(LCS)的最优子结构
2.递归解
根据1中的定理,在求X和Y的一个LCS时,我们需要求解一个或两个子问题。我们可以很容易的发现LCS问题的重叠子问题性质,为了求X和Y的一个LCS,我们可能需要求X和Y_{n-1}的一个LCS及X_{m-1}和Y的一个LCS。但是这几个子问题度包含求解X_{m-1}和Y_{n-1}的LCS的子子问题, 我们可以得到如下公式:
3.计算LCS的长度
伪代码如下:
m=X.length
n=Y.length
let b[1..m,1..n]andc[0..m,0..m]be new tables
for i =1 to m
c[i,0]=0
for j =0 to n
c[0,j]=0
for i=1 to m
for j=1 to n
if Xi==Yi
c[i,j]=c[i-1,j-1]+1
b[i,j]="A" //其实就是代表指向左上角的箭头
elseif c[i-1,j]>=c[i,j-1]
c[i,j]=c[i-1,j]
b[i,j]="B" //代表指向上方的箭头
else
c[i,j]=c[i,j-1]
b[i,j]="C" //代表指向左方的箭头
return c and b
c保存的是LCS的高度,b用于帮助构造最优解
4.构造LCS
PRINT_LCS(b,X,i,j)
if i==0 or j==0
return
if b[i,j]=="A"
PRINT_LCS(b,X,i-1,j-1)
elseif b[i,j]=="B"
PRINT_LCS(b,X,i-1,j)
else
PEINR_LCS(b,X,i,j-1)
下面是C++写的求解最长公共子序列长度代码
#include<iostream>
#include<string>
using namespace std;
int c[100][100];
int LCS_length(const char *X, const char *Y)
{
if(X == NULL||Y == NULL)
return 0;
int m = strlen(X);
int n = strlen(Y);
for(int i=1;i<=m;i++)
c[i][0]=0;
for(int j=1;j<=n;j++)
c[0][j]=0;
c[0][0]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(X[i] == Y[j])
c[i][j]=c[i-1][j-1]+1;
else
c[i][j]=max(c[i-1][j],c[i][j-1]);
}
for(int i=1;i<=m;++i)
{cout<<endl;
for(int j=1;j<=n;++j)
cout<<c[i][j]<<" ";
}
cout<<endl
return c[m][n];
}
int main()
{
char *a="ABCBDAB";
char *b="BDCABA";
int length;
length = LCS_length(a,b);
cout<<length<<endl;
}