参考:
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:f[i][j]代表前 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)。