动态规划解决最长公共子序列LCS问题

子序列的定义:给定一个序列X=<x1,x2,…,xm>,另一个序列Z=<z1,z2,…,zk>,满足如下条件时称为X的子序列,

即存在一个严格递增的X的下标序列<i1,i2,…,ik>和所有的j=1,2,…,k,满足Zj=X[ik]下标序列,使得它们都相等。

如<B,C,D,B>是X=<A,B,C,B,D,A,B>的子序列。

给定两个序列X和Y,如果Z既是X的子序列,也是Y的子序列,我们称它是X和Y的公共子序列。

LCS问题:给定两个序列,求X和Y长度最长的公共子序列。

分析:LCS具有最优子结构性质,两个序列的LCS包含两个序列的前缀的LCS。

定义C[0,…,m][0,….,n],m是X序列的长度,Y序列的长度是n。C[i][j]的含义是序列X[0,…,i-1]和序列Y[0,…,j-1]的LCS。

如果X[i-1]==Y[j-1],则c[i][j]=c[i-1][j-1]+1;如果X[i-1]!=Y[j-1],则序列X[0,…,i-1]和序列Y[0,…,j-1]的LCS有两种可能,

一种是序列X[0,…,i-2]和序列Y[0,…,j-1]的LCS,另一种是序列X[0,…,i-1]和序列Y[0,…,j-2]的LCS,两者取较大值。

即c[i][j]=max(c[i][j-1],c[i-1][j])。

下面是实现的过程:

// LCS.cpp : 定义控制台应用程序的入口点。
//最长公共子序列问题:给定两个序列X=和Y=,求X和Y的最长公共子序列。

#include "stdafx.h"
#include 
#include 
#include 
#include 
using namespace std;
//c[i][j]表示X的前i个字符和Y的前j个字符所共有的序列长度;b[i][j]用来回溯得到的公共子序列
void LCS_LENGTH(string X,string Y,vector> & b,vector> & c)
{
	int m=X.length(),n=Y.length();
	for(int i=0;i<=m;i++)
		c[i][0]=0;
	for(int j=0;j<=n;j++)
		c[0][j]=0;
	int i,j;
	for(i=1;i<=m;i++)
		for(j=1;j<=n;j++)
			if(X[i-1]==Y[j-1])
			{
				c[i][j]=c[i-1][j-1]+1;
				b[i][j]=2;
			}else if(c[i-1][j]>=c[i][j-1])
			{
				c[i][j]=c[i-1][j];
				b[i][j]=1;
			}else
			{
				c[i][j]=c[i][j-1];
				b[i][j]=0;
			}
}
//利用回溯和表b,追踪LCS
void  PRINT_LCS(vector> & b, string X,int xlen, int ylen,string &s)
{
	
	int i=xlen,j=ylen;
	if(i==0||j==0)
		return ;
	if(b[i][j]==2)//此时第i个元素X[i-1][j-1]属于公共元素
	{
		PRINT_LCS(b,X,i-1,j-1,s);
		s+=X[i-1];
	}else if(b[i][j]==1)//上面有c[i][j]=c[i-1][j];此时回溯c[i-1][j]
		PRINT_LCS(b,X,i-1,j,s);
	else
		PRINT_LCS(b,X,i,j-1,s);
}

int _tmain(int argc, _TCHAR* argv[])
{
	string X="ABCBDAB",Y="BDCABA";
	int m=X.length(),n=Y.length();
	vector>  b(m+1), c(m+1);
	string s;
	for(int i=0;i<=m;i++)
	{
		b[i].resize(n+1);
		c[i].resize(n+1);
	}
	 LCS_LENGTH(X,Y,b,c);
	 PRINT_LCS(b,X,m,n,s);
	 cout<<"X:"<

《动态规划解决最长公共子序列LCS问题》

它的时间复杂度是: O(m*n)   空间复杂度:O(m*n)

进一步优化:

动态规划解决LCS,一般要定义一个表c[0,…,m][0,…,n],所用的空间复杂的是O(m*n),其实这里表c的信息是冗余的。要想获得c[i][j]其实,只需根据c[i-1][j-1],c[i][j-1],c[i-1][j]中的信息获得即可。我们可以用O(m)或者O(n)的空间

就可以获得最长公共子序列的长度。我们这里定义一个数组a[m+1],存储如图中绿色部分的信息,a[0]=c[i-1][j-1],

当进行到比较X[i]与Y[j]时,我们要得到c[i,j]的值,它需要用到c[i-1][j-1],c[i][j-1],c[i-1][j],即a[0],a[i],a[i-1]。同时,它是新的a[i],如第二张图的红色部分所示。同时,原来a[0]里存储的数据用过后,再下一次循环就用不上了,a[0]也需要更新。然后,这次循环结束后,就形成了一个新的a数组。我们可以看到,它是从上到下,一列列的生成c[i][j]中的元素的。注意,当生成了一列结束,即j>=m时,开始新的外一层循环,此时,设置a[0]=0.

《动态规划解决最长公共子序列LCS问题》

《动态规划解决最长公共子序列LCS问题》

下面的代码在原有的基础增加了一个函数LCS(….),用O(m+1)的空间获得LCS长度,这里没有恢复重构LCS中的元素。

// LCS.cpp : 定义控制台应用程序的入口点。
//最长公共子序列问题:给定两个序列X=和Y=,求X和Y的最长公共子序列。

#include "stdafx.h"
#include 
#include 
#include 
#include 
using namespace std;
//这里对算法进行了优化,空间复杂度O(m+1)
void LCS(string X, string Y, vector & a)
{
	int m=X.length(),n=Y.length();
	 for (int j = 1; j <= n; j++) {
		 a[0]=0;//内层循环结束,a[0]要置零,想象求a[1][j]所依赖的a[0][j-1]=0,即是初始化步骤
            for (int i = 1; i <= m; i++) {
				int pre=a[0];
                int temp = a[i];
                if (X[i - 1] == Y[j - 1])
                    a[i] =pre+1;
                else a[i] = max(a[i],a[i-1]);
               a[0]=temp;
            }
		 }
}
//c[i][j]表示X的前i个字符和Y的前j个字符所共有的序列长度;b[i][j]用来回溯得到的公共子序列
void LCS_LENGTH(string X,string Y,vector> & b,vector> & c)
{
	int m=X.length(),n=Y.length();
	for(int i=0;i<=m;i++)
		c[i][0]=0;
	for(int j=0;j<=n;j++)
		c[0][j]=0;
	int i,j;
	for(i=1;i<=m;i++)
		for(j=1;j<=n;j++)
			if(X[i-1]==Y[j-1])
			{
				c[i][j]=c[i-1][j-1]+1;
				b[i][j]=2;
			}else if(c[i-1][j]>=c[i][j-1])
			{
				c[i][j]=c[i-1][j];
				b[i][j]=1;
			}else
			{
				c[i][j]=c[i][j-1];
				b[i][j]=0;
			}
}
//利用回溯和表b,追踪LCS
void  PRINT_LCS(vector> & b, string X,int xlen, int ylen,string &s)
{
	
	int i=xlen,j=ylen;
	if(i==0||j==0)
		return ;
	if(b[i][j]==2)//此时第i个元素X[i-1][j-1]属于公共元素
	{
		PRINT_LCS(b,X,i-1,j-1,s);
		s+=X[i-1];
	}else if(b[i][j]==1)//上面有c[i][j]=c[i-1][j];此时回溯c[i-1][j]
		PRINT_LCS(b,X,i-1,j,s);
	else
		PRINT_LCS(b,X,i,j-1,s);
}

int _tmain(int argc, _TCHAR* argv[])
{
	string X="ABCBDABEF",Y="BDCABAEF";
	int m=X.length(),n=Y.length();
	vector>  b(m+1), c(m+1);
	string s;
	for(int i=0;i<=m;i++)
	{
		b[i].resize(n+1);
		c[i].resize(n+1);
	}
	 LCS_LENGTH(X,Y,b,c);
	 PRINT_LCS(b,X,m,n,s);
	 cout<<"X:"< a(m+1);
	 LCS(X,Y ,a);
	 cout<


    原文作者:动态规划
    原文地址: https://blog.csdn.net/liuchenjane/article/details/52618783
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞