先来说明下什么是最长公共子序列,什么是是最长公共子串,举一个实际例子,myblogs与belong,最长公共子序列为blog(myblogs, belong),最长公共子串为lo(myblogs, belong)也就是说子串要求在原字符串中是连续的,而最长公共子序列则并不要求连续。
最长公共子序列问题
问题描述
给两个字条串X="ABCBDAB",Y="BDCABA",求这两个字符串的最长公共子序列。
问题分析
这里只求这个公共子序列的长度。 利用动态规划的思想分析。 定义一个前缀概念:
给定一个序列X = <x1,x2 ,..., xm>,对于i = 0,1,...,m,定义X的第i前缀为Xi = <x1,x2 ,..., xi>。例如,若 X = <A,B,C,B,D,A,B>,则 X4 = <A,B,C,B>,X0为空串。
则减小问题的规模,X、Y都去掉一个字符时,他们的最长公共子串和X与Y的最长公共子串有什么关系呢?
令X = <x0,x1,x2 ,...>
和Y = <y0,y1,y2 ,...>
为两个序列,Z =<z0,z1,z2 ,...
为X和Y的任意LCS。c[i,j]表示Xi和Yj的LCS的长度,则: - 如果Xi = Yj,则 c[i,j]=c[i-1,j-1]+1
。 - 如果 Xi ≠ Yj,则 c[i,j]=max(c[i-1,j],c[i,j-1])
此时要求i>0,j>0,即X、Y的字符个数都至少为两个,因为上面的递推公式都用到了c[i-1,j-1]
考虑初始条件,如果X,Y有一个为空串,则就没有公共子序列了。
如果X,Y有一个为只含一个字符的子符串,则公共子串要么是没有,可以看成是长度为0,要么是就是这个字符了其长度为1.
按这个思路,先计算初始条件,然后利用递推公式计算所有值,代码实现如下。
代码实现
int lcs(string word1, string word2) {
int size1 = word1.size();
int size2 = word2.size();
int max_length=0;
int dp[size1][size2];
//初值比较,字符串的第一个字符
if(word1[0] == word2[0]){
dp[0][0] =1;
max_length = 1;
}else{
dp[0][0] =0;
}
//字符串word2的一个字符,各word1的所有字符
for(int i = 1,j=0; i<size1; i++){
if(word1[i] == word2[j]){
dp[i][j] = 1;
max_length = 1;
}else{
dp[i][j] = dp[i-1][j];
}
}
//字符串word1的一个字符,各word2的所有字符
for(int j=1,i=0; j<size2; j++){
if(word1[i] == word2[j]){
dp[i][j] = 1;
max_length = 1;
}else{
dp[i][j] = dp[i][j-1];
}
}
//利用上面的初始化值来迭代
for(int i =1; i<size1; i++){
for(int j=1; j<size2;j++){
dp[i][j] =0;
if(word1[i] == word2[j]){
dp[i][j] = dp[i-1][j-1] +1;
}else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
if(dp[i][j] > max_length ){
max_length = dp[i][j] ;
}
}
}
return max_length;
}
最长公共子串问题
这里以leetcode上的一个最长公共子数组问题来说明。
问题描述
给数组A,B,求A,B的最长公共子数组,如 Input: A: [1,2,3,2,1] B: [3,2,1,4,7] Output: 3 解释,最长公共子数组为 [3, 2, 1]。
问题分析
和最长公共子序列问题类似,不过c[i][j]定义应该是稍有不同的,这里的c[i][j],表示A[0...i]和B[0...j]的最长公共子串,且此公共子串中一定包含A[i],B[j]的,最长公共子序列中的定义可以不包括A[i],B[j]的。
此时的递推公式也稍有不同了:
- 如果A[i] = B[j],则
c[i,j]=c[i-1,j-1]+1
。 - 如果 A[i] ≠ B[j],则
c[i,j]=0
代码实现
int findLength(vector<int>& A, vector<int>& B) {
int asize=A.size();
int bsize=B.size();
int dp[asize][bsize];
int max = 0;
for(int i=0,j=0; i<asize; i++){
if(A[i] == B[j]){
dp[i][j] = 1;
max = 1;
}else{
dp[i][j] = 0;
}
}
for(int i=0,j=0; j<bsize; j++){
if(A[i] == B[j]){
dp[i][j] = 1;
max = 1;
}else{
dp[i][j] = 0;
}
}
for(int i=1; i<asize; i++){
for(int j = 1; j<bsize; j++){
if(A[i] == B[j]){
dp[i][j] = dp[i-1][j-1] + 1;
}else{
dp[i][j] = 0;
}
if(dp[i][j] > max){
max = dp[i][j];
}
}
}
return max;
}
todo: - 补充c数组的表格 - 加求具体序列的方法