本篇博文提供了实现求解所有最长公共子序列的程序实现,并提供输出所有公共子序列的方法解释,需要具备基础知识是求解一个公共子序列的动态规划方法,请自行查阅相关资料。
子序列概念:设X=< x1, x2,┅, xm>,若有1≤i1< i2< ┅ <ik≤m,使得Z=< z1, z2,┅, zk> = < xi1, xi2,┅, xik>,则称Z是X的子序列,记为Z<X。
例如: X=<A,B,C,B,D,A,B>, Z=<B,C,B,A>, 则有Z<X。
公共子序列概念:设X,Y是两个序列,则有Z<X和Z<Y,则称Z是X和Y的公共子序列。
最长公共子序列:若Z<X,Z<Y,且不存在比Z更长的X和Y的公共子序列,则称Z是X和Y的最长公共子序列,记为 Z?LCS(X , Y)。
问题: 最长公共子序列一般可以有多个,求解并输出所有的LCS。
求解一个LCS比较容易,但求解所有的LCS比较繁琐,为了实现题目要求,程序引入了栈(Stack)存储结构用于存储具有两个方向的节点(i,j坐标),以及输出序列的存储数组,可实现了所有LCS的求解输出。
a) 本题根据二维数组C,记录Xi,Yj的LCS长度,然后根据矩阵B记录“↖”,“←”,“↑”,“←↑”四种情况,分别用整型数 3,-1,1,2表示,以便于记录;
b) 根据B矩阵求解LCS序列的规则,可以发现所有LCS路径构成了一个有向图,所以可以结合深度优先搜索算法并加以改进实现输出所有LCS;
c) 引入Stack(堆栈)存储结构(见程序 StackNode S[N];),S为结构体,包含i,j,pos三个参数,分别表示点的坐标,以及即将存入lcs的位置;同时分配动态数组(char*lcs =(char*)new char[len];)用于存储符合要求的数组;设置变量li用于记录要存入lcs数组的位置;
d) 算法核心即当遇到“←↑”情形,将B[i][j]=1,并将节点信息存入栈, 然后继续搜索下一个节点,当符合要求后输出序列,并回溯到最近的“←↑”节点(已经置为“↑”),并进行下一个序列的搜索输出,具体代码参见程序函数:void fn_OutputLCSes(int(*)[],char*,int(*)[],int, int)。
/*-----------------------------------------------------------------
* 输出所有LCSes
* -----------------------------------------------------------------
* By gujinjin
* 求解任意两个序列的所有最长公共子序列(LCS)
* Execute successfully on Visual Studio 2012
*/
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
// 定义数组最大长度
#define N 20
struct StackNode
{
int i;
int j;
int pos;
};
/*-----------------------------------------------------------------
* bool fn_Input(char*, int,char*,int)
* -----------------------------------------------------------------
*/
bool fn_Input(char* la, int na,char* lb,int nb)
{
// Reutrn false
if(na==0 || nb==0)return false;
// Input list
cout<<"Input La List:"<<endl;
for(int i=0;i<na;i++)
{
cin>>la[i];
}
cout<<"Input Lb List:"<<endl;
for(int j=0;j<nb;j++)
{
cin>>lb[j];
}
return true;
}
/*-----------------------------------------------------------------
* void fn_LCS(char*,char*,int(*)[],int, int)
* -----------------------------------------------------------------
* Mb中 1表示上箭头,-1表示右箭头,2表示上、右箭头,3表示斜箭头
*/
void fn_LCS(char* la,char* lb,int (*MC)[N],int (*Mb)[N],int na, int nb)
{
for(int i=0;i<=na;i++)MC[i][0]=0;
for(int j=1;j<=nb;j++)MC[0][j]=0;
for(int i=1;i<=na;i++)
{
for(int j=1;j<=nb;j++)
{
/* la,lb 是字母序列,从0开始而不是1 */
if(int(la[i-1]) == int(lb[j-1]))
{
MC[i][j]= MC[i-1][j-1]+1;
Mb[i][j]=3;
}
else if(MC[i-1][j] > MC[i][j-1])
{
MC[i][j] = MC[i-1][j];
Mb[i][j] = 1;
}
else if(MC[i-1][j] < MC[i][j-1])
{
MC[i][j] = MC[i][j-1];
Mb[i][j] = -1;
}
else
{
MC[i][j] = MC[i][j-1];
Mb[i][j] = 2;
}
} // end of for j
} // end of for i
}
/*-----------------------------------------------------------------
* void fn_OutputPreInfo(char*,char*,int(*[],int(*)[],int,int)
* -----------------------------------------------------------------
*/
void fn_OutputPreInfo(char* la, char* lb,int(*MC)[N],int(*Mb)[N],int na,int nb)
{
cout<<"La序列元素个数"<<na<<endl;
cout<<"La序列元素:"<<endl;
for(int i=0;i<na;i++)cout<<la[i]<<'\t';
cout<<endl;
cout<<"Lb序列元素个数"<<nb<<endl;
cout<<"Lb序列元素:"<<endl;
for(int j=0;j<nb;j++)cout<<lb[j]<<'\t';
cout<<endl;
cout<<"Output MC:"<<endl;
for(int i=0;i<=na ;i++)
{
for(int j=0;j<=nb ;j++)
{
cout<<MC[i][j]<<'\t';
}
cout<<endl;
}
cout<<"Output Mb"<<endl;
for(int i=0;i<=na ;i++)
{
for(int j=0;j<=nb ;j++)
{
cout<<Mb[i][j]<<'\t';
}
cout<<endl;
}
}
/*------------------------------------------
* 判断栈空函数
* ------------------------------------------
*/
int isempty(int s,int t)
{
if(s==t)return(1);/*栈为空,返回1 */
else return(0);
}
/*------------------------------------------
* 进栈函数
* ------------------------------------------
*/
void Push(StackNode s[],int& top,int i,int j,int pos)
{
s[top].i=i;
s[top].j=j;
s[top].pos=pos;
++top; //* 先入栈在++
}
/*------------------------------------------
* 出栈函数
* ------------------------------------------
*/
int Pop(int top)
{
return(--top);
}
/*-----------------------------------------------------------------
* void fn_OutputLCSes(int(*)[],char*,int(*)[],int, int)
* -----------------------------------------------------------------
*/
void fn_OutputLCSes(int (*Mb)[N], char* la, int i, int j,int len)
{
char *lcs =(char*)new char[len];
StackNode S[N];
int top=0,base=0,li;
li = len-1;
while(i>=0 && j>=0)
{
if(Mb[i][j]==2)
{
Push(S,top,i,j,li);
Mb[i][j]=1;
i=i;
j=j-1;
}
else if(Mb[i][j]==-1)
{
//Mb[i][j]=0;
i=i;
j=j-1;
}
else if(Mb[i][j]==1)
{
//Mb[i][j]=0;
i=i-1;
j=j;
}
else if(Mb[i][j]==3)
{
lcs[li]=la[i-1];
i=i-1;
j=j-1;
if(li==0)
{
for(int k=0;k<len;k++)cout<<lcs[k]<<'\t';
cout<<endl;
if(isempty(top,base))break;
else
{
top = Pop(top);
i=S[top].i;
j=S[top].j;
li = S[top].pos;
continue;
}
}
li--;
} // else if(Mb[i][j]==3)
}// while(1)
// 删除动态分配空间
delete [] lcs;
}
/*-----------------------------------------------------------------
* Main Fun
* -----------------------------------------------------------------
*/
void main(void)
{
// 定义俩序列
char la[N],lb[N];
// 定义俩辅助矩阵
int MC[N][N],Mb[N][N];
// Initialize
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
MC[i][j]=0;
Mb[i][j]=0;
}
int na,nb,len;
cout<<"Input your two list length:"<<endl;
cin>>na>>nb;
// 判断输入是否正确
if(fn_Input(la,na,lb,nb))
{
// 获取MC,Mb辅助矩阵
fn_LCS(la,lb,MC,Mb,na,nb);
// 输出矩阵信息
fn_OutputPreInfo(la,lb,MC,Mb,na,nb);
// 输出 LCS
cout<<"The LCSes are:"<<endl;
len = MC[na][nb];
//cout<< len<<endl;
fn_OutputLCSes(Mb,la,na,nb,len);
}
}
求解 X=<A,B,C,B,D,A,B>, Y=<B,D,C,A,B,A>,结果如下(控制台应用程序):
结果为: <B,D,A,B>,<B,C,A,B>,<B,C,B,A>
求解两个序列的所有最长公共子序列(LCSes),布布扣,bubuko.com
原文地址:http://blog.csdn.net/gujinjinseu/article/details/38278129