參考:
https://blog.csdn.net/kupepoem/article/details/104690520
https://blog.csdn.net/weixin_40673608/article/details/84262695
一、概述
相關概念
子序列形式化定義:
給定一個序列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)。