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



摘要

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

题目重述

子序列概念:设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

时间: 2024-11-08 19:12:17

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

求序列1和序列2的最长公共子序列-基于动态规划方法

1 #include <cstdlib> 2 #include <iostream> 3 #include <cstring> 4 5 using namespace std; 6 7 class maxlen_string 8 { 9 public: 10 void string_(); 11 void LCSLength(int,int,char*,char*,int**,int**); 12 void LCS(int,int,char*,int**); 13 ~m

POJ 2250(LCS最长公共子序列)

compromise Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Description In a few months the European Currency Union will become a reality. However, to join the club, the Maastricht criteria must be fulfilled, and this is n

LCS求最长公共子序列(DP)

动态规划并不是一种算法,而是一种解决问题的思路.典型的动态规划问题,如最长公共子序列(LCS),最长单调子序列(LIS)等. 动态规划分为四个步骤: 1.判断问题是否具有最优子结构 这里以LCS为例,X={x1,x2,...,xi}:Y={y1,y2,...,yj}.最长公共子序列Z={z1,z2,...,zk}: ①如果xi=yj,那么zk=xi=yj,且Zk-1是序列Xi-1和Yj-1的LCS: ②如果xi≠yj,那么zk≠xi:且Zk是序列Xi-1和Yj的LCS: ③如果xi≠yj,那么z

动态规划 - 最长公共子序列(LCS)

最长公共子序列也是动态规划中的一个经典问题. 有两个字符串 S1 和 S2,求一个最长公共子串,即求字符串 S3,它同时为 S1 和 S2 的子串,且要求它的长度最长,并确定这个长度.这个问题被我们称为 最长公共子序列问题. 与求最长递增子序列一样,我们首先将原问题分割成一些子问题,我们用 dp[i][j]表示 S1 中前 i 个字符与 S2 中前 j 个字符分别组成的两个前缀字符串的最 长公共子串长度. 显然的,当 i. j 较小时我们可以直接得出答案,如 dp[0][j]必 等于 0.那么,

hdu 1159 Common Subsequence(最长公共子序列,DP)

题意: 两个字符串,判断最长公共子序列的长度. 思路: 直接看代码,,注意边界处理 代码: char s1[505], s2[505]; int dp[505][505]; int main(){ while(scanf("%s%s",s1,s2)!=EOF){ int l1=strlen(s1); int l2=strlen(s2); mem(dp,0); dp[0][0]=((s1[0]==s2[0])?1:0); rep(i,1,l1-1) if(s1[i]==s2[0]) dp

hdu 1159 common sequence (最长公共子序列 dp)

http://acm.hdu.edu.cn/showproblem.php?pid=1159 题意 : 给出两个字符串 求出最长公共子序列 思路: if(str1[i]==str2[j]) { dp[i][j]=max(dp[i-1][j-1]+1,max(dp[i-1][j],dp[i][j-1])); } else dp[i][j]=max(dp[i-1][j],dp[i][j-1]); #include<cstdio> #include<cstring> #include&l

UVA 10723--Cyborg Genes+最长公共子序列变形

题目链接:点击进入 首先对于长度最短的情况是很容易确定的,只需要用两个字符串的长度和减去他们的最长公共子序列长度.然后比较麻烦的就是合乎要求的字符串的个数,其实我们也可以用类似于最长公共子序列的dp来求. 设dp[i][j]表示str1的前i个字符和str2的前j个字符所得到的满足要求的字符串,则如果str[i]==str[j],则dp[i][j]+=dp[i-1][j-1]; 否则就要根据i,j这两个位置上的最长公共子序列长度进行讨论,具体见代码. 代码如下: #include<iostrea

POJ 2250 &amp; UVA 531 Compromise(字符串、 最长公共子序列)

Compromise 题目: 题目大意: 这里有两篇短文,每篇短文有若干个单词,求这两篇短文中的最长公共子序列,并将其输出来! 没篇短文输入 为 "#" 时,结束该篇短文的输入. 这道题是多组测试数据,如果只写一组,那么就会 WA,我因为这就被 WA 了一次! 最长公共子序列的解法,就不多说了,基本上所有的算法书上都有介绍讲解. 这道题,题意和解法我认为都不是难点,我个人认为难点是在最长公共子序列的保存记录上. 对于最长公共子序列的记录保存上,我用了 C++ 的 STL 中的strin

动态规划-最长公共子序列LCS

0 问题 给定两个字符串,求最长公共子序列LCS. 也就是说两个字符串中都有的部分,或者理解为,两个字符串同时都删除字符串中的某些字符,使得最终的两个字符串,相等,且是最长的. 1 分析 假设两个str1,str2字符串,已经知道了最长公共子序列长度为L 那么,当在str1和str2,两个的尾部,同时添加一个相同的字符,比如a,那么新的str1,和str2的最长公共子序列长度就是L+1 当str1后面添加一个字符,str2不添加,那么最长公共子序列长度为L 反之,str1不添加,str2添加,那