最长公共子序列

一、最长公共子序列算法基本思想

最长公共子序列是基于动态规划的思想。

最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的(摘自百度百科)

我们可以分三个方面思考这样个问题:

1.如果x序列的最后一个元素和y序列的最后一个元素相同,那么这个元素必定是最长公共子序列的最后一个元素。

2.如果x序列和y序列的最后一个元素不同,那么问题就推给除了最后一个元素的x序列和完整y序列这两个序列的最长公共子序列(记为A),以及完整的x序列和除了最后一个元素的y序列这两个序列的最长公共子序列(记为B),max{A,B}就是原问题的解。

3.重复2步骤,递归下去,一直到x序列为空,或者y序列为空。

二、最长公共子序列的数据结构

c[i][j] 二维数组: 表示x从1到i的子序列和y从1到j的子序列的最长公共子序列。

b[i][j] 二维数组:表示x从1到i的子序列和y从1到j的子序列的最长公共子序列是由哪种情况下获得的。

value 1: 表示当xi == yi 字符的时候获得一个最长公共子序列的元素

value 2: 表示问题推给了x[1...i-1] 和 y[1...j]的最长公共子序列

value 3: 表示问题推给了x[1...i] 和 y[1...j-1]的最长公共子序列

三、最长公共子序列过程图解


四、算法源代码

#include <cstdio>

//序列x以及x的长度,字符串前面的空格是来填充的
//好让字符串首字母从下标1开始
char x[] = " ABCBDAB";
int lenx = 7;
//序列y以及y的长度
char y[] = " BDCABA";
int leny = 6;

//c[i][j]表示长度为i的x序列
//和长度为j的y序列的最大公共子序列
int c[8][7];

//记录c[i][j]的值是由哪个子问题产生的
int b[8][7];

//计算最长公共子序列的函数
//并且记录了最长公共子序列
int lcsLength();

//构建最长公共子序列
void lcs(int i, int j);

int main()
{
    printf("%d\n",lcsLength());
    lcs(lenx, leny);
    return 0;
}

int lcsLength()
{
    //如果x为空序列那么无论y取多长,x,y的最大公共子序列一定为0
    for (int i = 1; i<=lenx; i++) c[i][0] = 0;
    //如果y为空序列那么无论x取多长,x,y的最大公共子序列一定为0
    for (int i = 1; i<=leny; i++) c[0][i] = 0;
    for (int i=1;i<=lenx;i++){
        for (int j=1; j<=leny; j++) {
            //如果长度为i的x序列和长度为j的y序列的最后一个字符相同
            //那么最大公共子序列就是c[i-1][j-1]+1
            if(x[i] == y[j]){
                c[i][j] = c[i-1][j-1]+1;
                b[i][j] = 1;    //用1标记表示长度为i的x序列
                                //长度为j的y序列中最后一个字符是相同的
                
                
            //如果不等那么c[i][j] = max{c[i-1][j] , c[i][j-1]}
            }else if(c[i-1][j]>=c[i][j-1]){
                c[i][j] = c[i-1][j];
                b[i][j] = 2;
            }else{
                c[i][j] = c[i][j-1];
                b[i][j] = 3;
            }
        }
    }
    return  c[lenx][leny];
}

//通过b数组记录的信息构建最长公共子序列
void lcs(int i,int j)
{
    if(i==0||j==0)
        return;
    if (b[i][j] == 1) {
        lcs(i-1,j-1);
        printf("%c",x[i]);
    }else if(b[i][j] == 2){
        lcs(i-1,j);
    }else{
        lcs(i,j-1);
    }
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值