60行代码轻松解决最长公共子序列问题

参考:

https://blog.csdn.net/kupepoem/article/details/104690520

https://blog.csdn.net/weixin_40673608/article/details/84262695

https://blog.csdn.net/wwlcsdn000/article/details/78600167?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2

一、概述

相关概念
子序列形式化定义:
给定一个序列X=<x1,x2,x3,x4...,xm>,另一个序列Z=<z1,z2,z3,z4...,zk>,若存在一个严格递增的X的下标序列<i1,i2,i3,...,ik>对所有的1,2,3,...,k,都满足x(ik)=zk,则称Z是X的子序列

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

公共子序列定义:
如果Z既是X的子序列,又是Y的子序列,则称Z为X和Y的公共子序列

最长公共子序列(以下简称LCS):
2个序列的子序列中长度最长的那个

二、问题分解与解决

const char *strA = "BDCABA";
const char*strB = "ABCBDAB";

问题:求解字符串strA和strB的最长公共子序列

利用动态规划进行问题分解:

State:前 i和前j个字符的公共子串长度

Function :f[i][j]=max(f[i-1][j-1]+1,f[i-1][j],f[i][j-1])             strA[i]=strB[j]

                   f[i][j]=max(f[i-1][j],f[i][j-1])                               strA[i]!=strB[j]

初始化 :f[i][0]=0 f[0][j]=0 因为前0个字符与另外一个字符串的最长公共子序列是0

结果: f[strA.length()][strB.lenghth[]]

const char *strA = "BDCABA";
const char*strB = "ABCBDAB";
const int lenA = 6;
const int lenB = 7;
int main()
{
	int f[lenA+1][lenB+1];
	for (int i = 0; i <= lenA; i++)
		f[i][0] = 0;
	for (int j = 0 ; j <= lenB; j++)
		f[0][j] = 0;
	for (int i = 1; i <=lenA; i++)
	{
		for (int j = 1; j <=lenB; j++)
		{
			if(strA[i - 1] == strB[j - 1])
			{
				//f[i][j] = std::max( std::max(f[i - 1][j - 1] + 1, f[i - 1][j]), f[i][j - 1]);  
//通过证明可以简化为f[i][j] = f[i - 1][j - 1] + 1;具体可以看我之前的博客
				f[i][j] = f[i - 1][j - 1] + 1;
			}
			else
			{
			f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
			}
		}
	}

	int res = f[lenA][lenB];

	return 0;
}

上面只是得到了最长公共子序列的长度,如何得到最长公共子序列的内容?我们可以使用一个数组进行标记,然后根据标记进行记录公共子序列的内容。其实就是记录f[i][j] = f[i - 1][j - 1] + 1时的字符。根据标记进行检索。

const char *strA = "BDCABA";
const char*strB = "ABCBDAB";
const int lenA = 6;
const int lenB = 7;
int main()
{
	int f[lenA+1][lenB+1];
	int b[lenA + 1][lenB + 1];
	for (int i = 0; i <= lenA; i++)
		f[i][0] = 0;
	for (int j = 0 ; j <= lenB; j++)
		f[0][j] = 0;
	for (int i = 1; i <=lenA; i++)
	{
		for (int j = 1; j <=lenB; j++)
		{
			if(strA[i - 1] == strB[j - 1])
			{
				//f[i][j] = std::max( std::max(f[i - 1][j - 1] + 1, f[i - 1][j]), f[i][j - 1]);
				f[i][j] = f[i - 1][j - 1] + 1;
				b[i][j] = 3;
			}
		else
		{
			f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
			if (f[i - 1][j] > f[i][j - 1])
			{
				b[i][j] = 2;
			}
			else
			{
				b[i][j] = 1;
			}
		}
		}
	}

	int res = f[lenA][lenB];
	int i = lenA;
	int j = lenB;
	std::vector<char> vecLcs;
	vecLcs.resize(res);
	int index = res;
	while (i > 0 && j > 0)
	{
		if (b[i][j] == 3)
		{
			vecLcs[index-1]=strA[i-1];
			index--;
			i--;
			j--;
		}
		else if (b[i][j] == 2)
		{
			i--;
		}
		else if(b[i][j]==1)
		{
			j--;
		}
	}

	return 0;
}

从上面代码可以发现,我们是按照b[i][j]的值进行检索的。需要根据状态方程细细体会。最长公共子序列的结果是‘’BCBA‘’

三、算法时间复杂度分析
      时间复杂度:如果两个字符串的长度分别是 m、 n,那么算法时间复杂度为 Ο(m*n)。
      空间复杂度:空间复杂度主要为两个二维数组 f[][], b[][],占用的空间为 O(m*n)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章