博客
关于我
Java动态规划 实现最长公共子序列以及最长公共子字符串
阅读量:798 次
发布时间:2023-03-31

本文共 2478 字,大约阅读时间需要 8 分钟。

动态规划法是一种在解决复杂问题时非常有效的方法,通过将大问题分解成多个子问题,并存储子问题的解,以避免重复计算。对于求解两个字符序列的最长公共子序列(LCS)问题,动态规划法提供了一种高效的解决方案。

动态规划法的基本原理

  • 问题分解:动态规划法通过递归地将问题分解成更小的子问题,并将每个子问题的解存储起来,以避免重复计算。这使得在解决大规模问题时,算法的时间复杂度显著降低。

  • 二维数组的使用:为了记录每个子问题的解,通常使用一个二维数组c,其中c[i][j]表示X的前i个字符和Y的前j个字符的LCS的长度。另一个二维数组b用于记录c[i][j]的来源,以便在回溯过程中重建LCS的路径。

  • 递推公式

    • 如果X的第i个字符等于Y的第j个字符,那么c[i][j] = c[i-1][j-1] + 1。
    • 如果X的第i个字符不等于Y的第j个字符,那么c[i][j] = max(c[i-1][j], c[i][j-1])。
  • 方向数组b:b[i][j]记录c[i][j]的来源:

    • 如果X[i] == Y[j],则b[i][j] = 1,表示c[i][j]来自c[i-1][j-1]。
    • 如果c[i-1][j] > c[i][j-1],则b[i][j] = 0,表示c[i][j]来自c[i-1][j]。
    • 否则,b[i][j] = -1,表示c[i][j]来自c[i][j-1]。
  • 回溯构建LCS路径

    通过方向数组b,可以从c[m][n]的值开始,向上或向左移动,直到遇到边界条件(i=0或j=0)。在这个过程中,记录每一步的字符位置,从而重建LCS的具体路径。

    算法分析

    动态规划法的时间复杂度为O(nm),其中n和m分别是两个字符序列的长度。这是因为每个子问题的解都只需计算一次,最多访问m×n次子问题的解。空间复杂度为O(nm),由于使用了二维数组来存储c和b数组。

    Java代码实现

    以下是实现LCS问题的Java代码:

    public class LCSProblem {    public static void main(String[] args) {        String[] x = { "", "A", "B", "C", "B", "D", "A", "B" };        String[] y = { "", "B", "D", "C", "A", "B", "A" };        int[][] b = getLength(x, y);        Display(b, x, x.length - 1, y.length - 1);    }    public static int[][] getLength(String[] x, String[] y) {        int[][] c = new int[x.length][y.length];        int[][] b = new int[x.length][y.length];        for (int i = 1; i < x.length; i++) {            for (int j = 1; j < y.length; j++) {                if (x[i].equals(y[j])) {                    c[i][j] = c[i - 1][j - 1] + 1;                    b[i][j] = 1;                } else {                    if (c[i - 1][j] > c[i][j - 1]) {                        c[i][j] = c[i - 1][j];                        b[i][j] = 0;                    } else {                        c[i][j] = c[i][j - 1];                        b[i][j] = -1;                    }                }            }        }        return b;    }    public static void Display(int[][] b, String[] x, int i, int j) {        if (i == 0 || j == 0) {            return;        }        if (b[i][j] == 1) {            Display(b, x, i - 1, j - 1);            System.out.print(x[i] + " ");        } else if (b[i][j] == 0) {            Display(b, x, i - 1, j);            System.out.print(x[i] + " ");        } else {            Display(b, x, i, j - 1);            System.out.print(x[i] + " ");        }    }}

    输出结果

    运行上述代码后,输出结果如下:

    000000000010000000002000001000300000000000000000000010000000100000000020001000003第1个公共子串: ing 第2个公共子串: ven

    结论

    通过上述动态规划法,我们可以高效地求解两个字符序列的最长公共子序列问题。该方法通过分治和记忆化优化了算法的时间复杂度,使其能够在较短时间内处理较长的输入序列。

    转载地址:http://fpefk.baihongyu.com/

    你可能感兴趣的文章
    OSG中找到特定节点的方法(转)
    查看>>
    OSG学习:C#调用非托管C++方法——C++/CLI
    查看>>
    OSG学习:OSG组成(三)——组成模块(续):OSG核心库中的一些类和方法
    查看>>
    OSG学习:OSG组成(二)——渲染状态和纹理映射
    查看>>
    OSG学习:WIN10系统下OSG+VS2017编译及运行
    查看>>
    OSG学习:人机交互——普通键盘事件:着火的飞机
    查看>>
    OSG学习:几何体的操作(一)——交互事件、简化几何体
    查看>>
    OSG学习:几何体的操作(二)——交互事件、Delaunay三角网绘制
    查看>>
    OSG学习:几何对象的绘制(一)——四边形
    查看>>
    OSG学习:几何对象的绘制(三)——几何元素的存储和几何体的绘制方法
    查看>>
    OSG学习:几何对象的绘制(二)——简易房屋
    查看>>
    OSG学习:几何对象的绘制(四)——几何体的更新回调:旋转的线
    查看>>
    OSG学习:场景图形管理(一)——视图与相机
    查看>>
    OSG学习:场景图形管理(三)——多视图相机渲染
    查看>>
    OSG学习:场景图形管理(二)——单窗口多相机渲染
    查看>>
    OSG学习:场景图形管理(四)——多视图多窗口渲染
    查看>>
    OSG学习:新建C++/CLI工程并读取模型(C++/CLI)——根据OSG官方示例代码初步理解其方法
    查看>>
    Sql 随机更新一条数据返回更新数据的ID编号
    查看>>
    OSG学习:空间变换节点和开关节点示例
    查看>>
    OSG学习:纹理映射(一)——多重纹理映射
    查看>>