当前位置: 首页 > article >正文

[动态规划]最长公共子序列

题目:

        若给定序列X={x1,x2,…,xm},则另一序列Y={y1,y2,…,yk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:yj=xij。例如,序列Y={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。

        给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

        最长公共子序列(LCS)采用动态规划算法的模板如下:

//最长公共子序列长度
void LCSLength(char x[],char y[],int m,int n){
    for(int i = 0;i <= m;i++){  //当y序列为空时 c[i][0]为0
        c[i][0] = 0;
    }
    for(int j = 0;j <= n;j++){  //当x序列为空时 c[0][j]为0
        c[0][j] = 0;
    }

    for(int i = 1;i <= m;i++){
        for(int j = 1;j <= n;j++){
            if(x[i] == y[j]){  //两个序列当前元素相同  取斜上方的值+1
                c[i][j] = c[i-1][j-1] + 1;
            }else if(c[i][j-1] >= c[i-1][j]){  //不相同 比较上方和左方值的大小
                c[i][j] = c[i][j-1];
            }else if(c[i][j-1] < c[i-1][j]){
                c[i][j] = c[i-1][j];
            }
        }
    }
}

//最长公共子序列
void LCS(int i, int j){
    if(i == 0 || j == 0){
        return;
    }

    if(b[i][j] == 1){  // x[i] == y[j]  斜上方找下一个相同的元素
        LCS(i-1,j-1);
        cout<<x[i]<<" ";
    }else if(b[i][j] == 2){  // c[i][j-1] >= c[i-1][j] 向左找
        LCS(i,j-1);
    }else if(b[i][j] == 3){  // c[i][j-1] < c[i-1][j] 向右找
        LCS(i-1,j);
    }
}

        状态转移方程为:

  • i = 0,j = 0 说明有一个(或两个)序列为空,所以最长公共子序列长度为0;
  • x[i] = y[j] 说明此时两个序列当前元素相同,那么找到( x[i-1],y[j-1] )的最长公共子序列长度即可,所以 c[i][j] = c[i-1][j-1] + 1。
  • x[i] != y[j] 说明此时两个序列当前元素不相同,那么找到( x[i-1],y[j] )或( x[i],y[j-1] )的最长公共子序列长度即可,取较大值。所以c[i][j] = max(c[i-1][j],c[i][j-1])。

01026e0abe3942a8be55d1183ee2669f.png

        下面给大家举一个例子,方便理解:

        假设 x 序列 的元素为{A,B,C,B,D,A,B},y 序列的元素为{B,D,C,A,B,A}。我们可以画出对应的二维表,如下图所示。

f89446d75f2344aa92e6bd440f8501ed.png

        接下来我们开始动态规划。假设有一个序列为空时,最长公共子序列长度 c[i][j] 的值为0。(没有元素相同,所以最长公共子序列长度为0) 。

e9f70b658be74129885801672eab8799.png

        接下来双重 for 循环开始遍历剩下的所有点。在坐标(x[1],y[4])处,两个序列此时的元素相同,所以取斜上方的值+1。在坐标(x[1],y[5])处,两个序列此时的元素不相同,所以比较上方和左方值的大小,取两者中大的。

aeebe4b3d2f74bdab34834b4fac6e0f8.png

        按照上述方法,可以补全最长公共子序列长度 c[i][j]。

ed4a8f91c8bd41c9b7aba0899901b1c6.png

        以上求解的是最长公共子序列长度,接下来我们讨论最长公共子序列。 在代码中,我们已经将 c[i][j] 的来源的每一种情况都记录在了 b[i][j] 中。注意,找最长公共子序列应该从b[m][n]开始找,也就是最后一个格子。

  • 如果 b[i][j] == 1,表示 x[i] == y[j],所以向斜上方找上一个相同的元素。
  • 如果 b[i][j] == 2,表示当前元素的 c[i][j] 是因为 c[i][j-1] >= c[i-1][j],所以向左找上一个相同的元素。
  • 如果 b[i][j] == 3,表示当前元素的 c[i][j] 是因为 c[i][j-1] < c[i-1][j],所以向上找上一个相同的元素。

        上面的图是根据代码的逻辑画的,但这有一个缺点,它只能找到众多可能中的一个最长公共子序列。 

        下面是一种可以找到所有可能的最长公共子序列的方法,大家可以参考。

        希望大家学有所获!


http://www.kler.cn/a/392631.html

相关文章:

  • qt QKeySequence详解
  • 结构体是否包含特定类型的成员变量
  • C#发票识别、发票查验接口集成、电子发票(航空运输电子行程单)
  • 数据库SQL——连接表达式(JOIN)图解
  • 如何从头开始构建神经网络?(附教程)
  • 2024版本IDEA创建Sprintboot项目下载依赖缓慢
  • vue 计算属性get set
  • 白酒除高级醇提升口感工艺
  • Javascript高级—如何实现一个类型判断函数?
  • 基于复现油炸鸡的智能手表的过程(1)
  • windows工具 -- 使用rustdesk和云服务器自建远程桌面服务, 手机, PC, Mac, Linux远程桌面 (简洁明了)
  • 前端-同源与跨域
  • 【解决】Layout 下创建槽位后,执行 Image 同步槽位位置后表现错误的问题。
  • 为什么RNN(循环神经网络)存在梯度消失和梯度爆炸?
  • 自动驾驶系列—自动驾驶车辆的姿态与定位:IMU数据在复杂环境中的关键作用
  • Python PyQt5 实现 .his 文件批量转 Excel 工具
  • 代码版本管理艺术
  • 【Python TensorFlow】进阶指南(续篇一)
  • SpringCloud学习笔记
  • LeetCode【0021】合并两个有序链表
  • HBuilder(uniapp) 配置android模拟器
  • php回调函数(匿名)的使用
  • IC 脚本之VIM 记录
  • 构建高效在线商店:Spring Boot框架应用
  • three.js 杂记
  • mysql 常用命令(二)