版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Neo_kh/article/details/81274321
最长公共子序列问题(LCS)
公共子序列的意思是在两个序列中,按顺序找出共同出现的元素,这些元素按顺序排列就是原序列的子序列:例如A=zxyxyz,B=xyyzx;则A和B的一个公共子序列为:xyy,而最长公共子序列是:xyyz。因此,A和B的LCS长度为4.
POJ和HDU上面有LCS的题目,下边给出入门级别的题目:
(1)先说一下暴力方案( • ̀ω ⁃᷄)✧
- 设序列A长度为n,序列B长度为m。
- 列举出A所有的 个子序列,对于每一个子序列在 时间内来确定它是否也是B的子序列。
很明显,此算法的时间复杂度
这个方案想法简单,但很可能TLE(time limited exceed),所以就有了下面的算法———
(2)动态规划 Σ>―(〃°ω°〃)♡→
为了方便理解问题,我们约定序列的起始地址为1,而不是0。
首先我们寻找求LCS长度的状态转移方程(也就是递推式)。
令
,
.
令
表示
和
的最长公共子序列的长度。
注意
和
可能是0,此时,
和
中的一个或同时为空串。即如果
或
,那么
.
容易得出以下LCS递推式:
递推式解释:
- 我们假设无论是 还是 都已经解出。现在来求 。
- 当 and 时,意味着A和B对应位置相等,因此该位置也是LCS的一部分,LCS长度+1。
- 当
and
时,意味着当前遍历到的序列对应位置不相等。而
的值必定是
a1a2...ai-1
与b1b2..bj
的LCS长度(即 ) 和a1a2...ai
与b1b2..bj-1
的LCS长度(即 )中的较大者。
现在,我们构建一个(n+1)x(m+1)表来计算L[i][j],只需要用上面的公式逐行填表。见下面C++代码
#include<iostream>
#include<string>
#define Max(a,b) ((a)>(b))?(a):(b)
using namespace std;
string A,B;
int L[100][100];
int main(){
int n,m;
int i,j;
cin>>A>>B;//读取字符串
n=A.length();//计算长度
m=B.length();
for(i=0;i<=n;i++){//初始化j=0的情况
L[i][0]=0;
}
for(j=0;j<=m;j++){//初始化i=0的情况
L[0][j]=0;
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(A[i-1]==B[j-1])
L[i][j]=L[i-1][j-1]+1;
else
L[i][j]=Max(L[i][j-1],L[i-1][j]);
}
}
cout<<L[n][m]<<endl;
return 0;
}
程序运行结果如图:
把上面的代码修改一下就可以刷POJ和HDU的那两道题啦。