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)。

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