求解两个序列的所有最长公共子序列(LCSes)

来源:互联网 发布:唱谱软件安卓版 编辑:程序博客网 时间:2024/06/07 23:34


摘要

本篇博文提供了实现求解所有最长公共子序列的程序实现,并提供输出所有公共子序列的方法解释,需要具备基础知识是求解一个公共子序列的动态规划方法,请自行查阅相关资料。


题目重述

子序列概念:设X=< x1, x2,, xm>,若有1i1< i2< <ikm,使得Z=< z1, z2,, zk> = < xi1, xi2,, xik>,则称ZX的子序列,记为Z<X

      例如: X=<A,B,C,B,D,A,B>, Z=<B,C,B,A>,  则有Z<X

公共子序列概念:设X,Y是两个序列,则有Z<XZ<Y,则称ZXY的公共子序列。

最长公共子序列:若Z<XZ<Y,且不存在比Z更长的XY的公共子序列,则称ZXY的最长公共子序列,记为ZÎLCS(X , Y)

问题:最长公共子序列一般可以有多个,求解并输出所有的LCS


问题分析

求解一个LCS比较容易,但求解所有的LCS比较繁琐,为了实现题目要求,程序引入了栈(Stack)存储结构用于存储具有两个方向的节点(ij坐标),以及输出序列的存储数组,可实现了所有LCS的求解输出。


算法思想

a)本题根据二维数组C,记录XiYjLCS长度,然后根据矩阵B记录“↖”,“←”,“↑”,“←↑”四种情况,分别用整型数 3,-1,1,2表示,以便于记录;

b)根据B矩阵求解LCS序列的规则,可以发现所有LCS路径构成了一个有向图,所以可以结合深度优先搜索算法并加以改进实现输出所有LCS

c)引入Stack(堆栈)存储结构(见程序StackNode S[N];),S为结构体,包含ij,pos三个参数,分别表示点的坐标,以及即将存入lcs的位置;同时分配动态数组(char*lcs =(char*)newchar[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 20struct 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 falseif(na==0 || nb==0)return false;// Input listcout<<"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];// Initializefor(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);// 输出 LCScout<<"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>


0 0
原创粉丝点击