转载:
作者 :JarvisChu
出处:http://blog.csdn.net/jarvischu
最长公共子序列
问题
若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。
分析
序列X和Y使用数组表示:X[1…N],Y[1…N],使用二维数组Z[N+1][M+1]定义解,其中Z[i][j] 表示 {X1,…,Xi}与{Y1,…,Yj}的最长公共子序列长度。如:Z[1][2]表示 {X1} 与 {Y1,Y2}
由上面的定义可知:
Z[i][0] 表示X{…} 与 空序列Y {} 的公共子序列,故Z[i][0] = 0
Z[0][j] 表示空序列X {}{} 与Y {…} 的公共子序列,故Z[0][j]= 0
递推公式如下:
为了求得最长公共子序列的具体值,需要在求解公共子序列长度的时候,添加标志(或称为备忘)以记录求解过程的每一步,便于在得到长度后,回溯求解过程,得到具体的序列。
下面的代码中使用how[N+1][M+1] 完成这一功能。
代码:
[cpp] view plain copy
/************************************************************************
* 名 称:LCS.cpp
* 功 能:动态规划算法案例:最长公共子序列
* 作 者:JarvisChu
* 时 间:2013-11-8
************************************************************************/
#include <iostream>
#define N 8
#define M 7
char X[N+1]={' ','A','B','C','D','E','F','G','H'};// 序列 X X0不用
char Y[M+1]={' ','B','D','G','H','A','F','A'}; // 序列 Y Y0不用
//#define N 4
//#define M 3
//char X[N+1]={' ','A','B','C','D'};// 序列 X X0不用
//char Y[M+1]={' ','A','B','D'}; // 序列 Y Y0不用
int Z[N+1][M+1]; //Z[i][j] 表示 {X1,...,Xi}与{Y1,...,Yj}的最长公共子序列长度
//Z[0][0] 表示 {} 与 {}
//Z[1][2] 表示 {X1} 与 {Y1,Y2}
int how[N+1][M+1]; //记录最长公子序列是如何得到的,用来追溯Z,得到具体的公共子序列
//how[i][j] = 0 表明记录 Z[i][j] = Z[i-1][j-1]+1, X[i] == Y[j] 时;
//how[i][j] = 1 表明记录 Z[i][j] = Z[i-1][j], X[i] != Y[j] 时;
//how[i][j] = 2 表明记录 Z[i][j] = Z[i][j-1], X[i] != Y[j] 时;
/*----------------------------------------------------------------------------------
* 功 能: 求序列 X和Y的最长公共子序列的长度 [求最优解的值]
* 参 数:
* 返 回:无
------------------------------------------------------------------------------------*/
void LCSLength()
{
int i,j;
//X的{} 和 Y的{}{Y1}...{Y1,Y2}的公共子序列长度为0;同理有Y的{}
for(i=0;i<N;i++) Z[i][0] = 0;
for(j=0;j<M;j++) Z[0][j] = 0;
//考虑其他情况,X和Y都顺序增长
for(i=1;i<=N;i++)
{
for(j=1;j<=M;j++)
{
//X,Y当前元素相等,公共子序列长度加1
if(X[i] == Y[j])
{
Z[i][j] = Z[i-1][j-1]+1;
how[i][j] = 0;
}
//Z[i][j] = max(Z[i-1][j],Z[i][j-1]);
else if(Z[i-1][j] >= Z[i][j-1])
{
Z[i][j] = Z[i-1][j];
how[i][j] = 1;
}
else
{
Z[i][j] = Z[i][j-1];
how[i][j] = 2;
}
}
}
}
/*----------------------------------------------------------------------------------
* 功 能: 追溯Z,得到具体的公共子序列 [求最优解]
* 参 数:
* 返 回:无
------------------------------------------------------------------------------------*/
void LCS(int i,int j)
{
if(i==0 || j==0) return ;
if(how[i][j] == 0)
{
LCS(i-1,j-1);
std::cout<<X[i]<<" ";
}
else if(how[i][j] == 1)
{
LCS(i-1,j);
}
else
{
LCS(i,j-1);
}
}
int main()
{
//最长公共子序列的长度
LCSLength();
std::cout<<Z[N][M]<<std::endl;//长度
//for(int i=0;i<=N;i++)
//{
// for(int j=0;j<=M;j++)
// {
// std::cout<<Z[i][j]<<" ";
// }
// std::cout<<std::endl;
//}
LCS(N,M);
return 0;
}