最长公共子序列(LCS)

定义:

1,一个序列S任意删除若干个字符得到新序列T,则T叫做S的子序列;

2,两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列。

例:

字符串13455与245576的最长公共子序列为455

字符串acdfg与adfc的最长公共子序列为adf

3,注意区别最长公共子串(Longest Common Substring)

最长公共子串要求连续

 

字符串X,长度为m,从1开始数;

字符串Y,长度为n,从1开始数;

Xi 为字符串X的i前缀,

Yj为字符串Y的j前缀;

LCS(X,Y)为字符串X和Y的公共子序列集合。

 

LCS解法探索

 

第一种情况: Xm = Yn;

若Xm = Yn(最后一个字符相同)

LCS(Xm, Yn) = LCS(Xm-1, Yn-1) + Xm;

这样我们解题的规模就减小了。

 

例:

LCS( BDC, ABC) = LCS( BD,AB) + 'C'

 

第二种情况:Xm != Yn

 

我们还是分类讨论:

如果Xm != Yn;

要么 LCS(Xm,Yn) = LCS(Xm-1,Yn); 丢弃Xm;

要么LCS(Xm,Yn) = LCS(Xm,Yn-1); 丢弃Yn;

 

这样我们只需求他们两个中最长的即可。

LCS(Xm,Yn) = max(LCS(Xm-1,Yn), LCS(Xm, Yn-1));

例:

LCS( BD,AB) = max(LCS(BD,A), LCS(B,AB));

 

这样我们可以得到:

 

LCS(Xm, Yn) =  LCS(Xm-1,Yn-1) + Xm;      当Xm = Yn;

         max( LCS(Xm-1), LCS(Xm,Yn-1) );  当Xm != Yn;

 

显然,这就是动态规划问题

 

我们用一个二维数组chess[8][7]来表示:

X = "ABCBDAB"

Y = "BDCABA"

 

1,我们把第0行,第0列都初始化为0;表示和空串求最长子串,

2,我们比较X1(A) 和Y1(B), 可以看到不相等,那么就调用max(LCS(X1,Y1)),X1 和 Y1 都为0,所以chess[x1][y1] = max(x1,y1) = 0

3,X1  != Y2, X1 != Y3, 所以都为0

4,当X1 == Y4== "A"时,我们调用相等时的式子,LCS(Xm,Yn) = LCS(Xm-1,Yn-1) + Xm; 所以chess[x1 = 1][Y4 = 4] = chess[x0 = 0][Y3 = 3] + 1;(相当于它的左上的值+1)

5, 当(X1 = A) != (Y5 = B)时,就是它的左边和上边的值求大者,max(0,1) = 1; 所以chess[1][5] =  max(chess[1][4] + chess[0][5] ) = 1;

6,后面根据前面可以自己算出

 

  0123456 
  YjBDCABA 
0Xi0000000 
1A000011  
2B0       
3C0       
4B0       
5D0       
6A0       
7B0       

 

 

 

 

 

 

 

 

 

 

 

 

 

完整的结果如下:

 

  012345
  YjBDCABA 
0Xi0000000 
1A000011 
2B0 1 1 1 1 2 2 
3C0 1 1 2 2 2 2 
4B0 1 1 2 2 3 3 
5D0 1 2 2 2 3 3 
6A0 1 2 2 3 3 4 
7B0 1 2 2 3 4 4

 

 

 

 

 

 

 

 

 

 

 

 

 

这样我们得到了最长的子串数目,但是我们应该如何表达呢?

我们从后面往前看,也就是绿色数字(ABCB)

最后我们在反转下就行了.(BCBA).

 

//
//  main.cpp
//  c++prime
//
//  Created by SJCHEN on 2019/1/19.
//  Copyright © 2019 SJCHEN. All rights reserved.
//

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

void LCS(const char* a, const char*b, string &str)
{
    int len1 = (int)strlen(a);
    int len2 = (int)strlen(b);
    const char* s1 = a - 1;//从1开始数
    const char* s2 = b - 1;
    vector<vector<int>> chess(len1 + 1,vector<int>(len2+1));//二维数组
    int i, j;
    for (i = 0; i <= len1; i++)//第0列
        chess[i][0] = 0;
    for(j = 0; j <= len2; j++)//第0行
        chess[0][j] = 0;
    for (i = 1; i <= len1; i++) {
        for (j = 1; j <= len2; j++) {
            if(s1[i] == s2[j])
                chess[i][j] = chess[i-1][j-1] + 1;
            else
                chess[i][j] = max(chess[i][j-1], chess[i-1][j]);
        }
    }
    i = len1;
    j = len2;
    while (i != 0 && j != 0) {
        if (s1[i] == s2[j]) {
            str.push_back(s1[i]);
            i--;
            j--;
        }
        else {
            if (chess[i][j-1] > chess[i-1][j])
                j--;
            else
                i--;
        }
    }
    reverse(str.begin(), str.end());//反转
}

int main()
{
    char *a,*b;
    string str;
//    cin >> a >> b;
    a = "ABCBDAB";
    b = "BDCABA";
    LCS(a,b,str);
    cout << str << endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/SJCHEN/p/10573730.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值