Java基于动态规划法实现求最长公共子序列及最长公共子字符串示例

Java基于动态规划法实现求最长公共子序列及最长公共子字符串示例攻略

什么是最长公共子序列?

两个序列 X 和 Y 的“公共子序列”,是指存在一个序列 Z,Z 中元素既在 X 中,也在 Y 中,在 X 和 Y 中出现的次序分别相同,且 Z 的长度最大。例如序列“12345”和“1245”的公共子序列有“12”、“145”等,其中“145”最长,长度为 3,即为“12345”和“1245”的最长公共子序列(Largest Common Subsequence,LCS)。

什么是最长公共子串?

两个字符串 X 和 Y 的“公共子串”,是指在 X 和 Y 中都出现过的连续字符串。例如字符串“Hello World”和“Hella World”的公共子串有“Hell”、“ello”等,其中“ello”最长,长度为 4,即为“Hello World”和“Hella World”的最长公共子串(Longest Common Substring,LCS)。

如何求最长公共子序列?

我们可以利用动态规划(Dynamic Programming,DP)来解决这个问题。

动态规划法解决问题的一般步骤:

  1. 问题转化:将原问题转化为子问题。
  2. 定义状态:定义状态方程。
  3. 确定状态转移方程:根据子问题之间的关系,确定通项公式。
  4. 分析复杂性:定义好状态和状态转移方程后,问题的时间复杂度通常比较容易分析。

最长公共子序列-算法分析

众所周知,两个序列的最长公共子序列问题可以用动态规划算法求解。具体思想是,设序列 X 和 Y 的长度分别为 m 和 n,则先用 m 行 n 列的矩阵 dp 存储 X 和 Y 之间任意部分的 LCS 长度,其中 dp[i][j] 表示 X[0~i] 和 Y[0~j] 的 LCS 的长度。接下来,序列X和Y的任何LCS都是矩阵 dp[m][n] 中的数字。

例如,X = “fasdugnisarefhadseo” ,Y = “sduguarsdewq” ,LCS 就是 “sdug”,长度为 4。

实现如下:

public static String LCSLength(String str1, String str2) throws Exception {
        if (str1.isEmpty() || str2.isEmpty()){
            throw new Exception("cannot input null...");
        }

        int len1 = str1.length();
        int len2 = str2.length();
        int[][] LCSLength = new int[len1 + 1][len2 + 1];

        for (int i = 0; i <= len1; i++) {
            for (int j = 0; j <= len2; j++) {
                if (i == 0 || j == 0) {
                    LCSLength[i][j] = 0;
                } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    LCSLength[i][j] = LCSLength[i - 1][j - 1] + 1;
                } else {
                    LCSLength[i][j] = Math.max(LCSLength[i - 1][j], LCSLength[i][j - 1]);
                }
            }
        }

        int index = LCSLength[len1][len2];
        char[] lcs = new char[index];
        int i = len1, j = len2;
        while (i > 0 && j > 0) {
            if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                lcs[--index] = str1.charAt(i - 1);
                i--;
                j--;
            } else if (LCSLength[i - 1][j] > LCSLength[i][j - 1]) {
                i--;
            } else {
                j--;
            }
        }
        return new String(lcs);
    }

最长公共子串-算法分析

在实现最长公共子串的时候,我们需要将问题转化为动态规划问题。我们需要用矩阵 dp 记录当前考虑的任意两个子串的最长公共子串长度,其中 dp[i][j] 表示 str1 的前 i 个字符和 str2 的前 j 个字符的最长公共子串长度。由于最长公共子串要求连续,所以在考虑 str1 和 str2 的第 i 个和第 j 个字符的时候,我们如果两者相等,就可以根据 dp[i - 1][j - 1] + 1 更新当前的 dp[i][j],否则 dp[i][j] = 0。

例如,str1 = “fasdugnisarefhadseo” ,str2 = “sduguarsdewq” ,LCS 就是 “sdug”,长度为 4。

实现如下:

class Main {
    public static String LCSubstring(String str1, String str2) throws Exception {
        if (str1.isEmpty() || str2.isEmpty()){
            throw new Exception("cannot input null...");
        }
        int[][] dp = new int[str1.length() + 1][str2.length() + 1];
        int maxLen = 0, maxEnd = str1.length(), index = str1.length();
        for (int i = 1; i <= str1.length(); i++) {
            for (int j = 1; j <= str2.length(); j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                if (dp[i][j] > maxLen) {
                    maxLen = dp[i][j];
                    maxEnd = i;
                }
            }
        }
        return str1.substring(maxEnd - maxLen, maxEnd);
    }
}

示例说明

示例 1:求两个字符串的最长公共子序列

输入:

String str1 = "fasdugnisarefhadseo";
String str2 = "sduguarsdewq";
String lcs = LCSLength(str1, str2);
System.out.println(lcs);

输出:

sdug

示例 2:求两个字符串的最长公共子串

输入:

String str1 = "fasdugnisarefhadseo";
String str2 = "sduguarsdewq";
String lcs = LCSubstring(str1, str2);
System.out.println(lcs);

输出:

sdug

总结

以上就是动态规划求解最长公共子序列和最长公共子串的完整攻略,其中给出了两个示例分别演示了如何使用代码求解最长公共子序列和最长公共子串。在实际编程中,我们可以利用动态规划算法解决这个问题,使用矩阵 dp 记录当前问题的状态和解,用通用公式进行状态转移,从而得出最优解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java基于动态规划法实现求最长公共子序列及最长公共子字符串示例 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Jenkins+tomcat自动发布的热部署/重启及遇到的问题解决办法(推荐)

    下面详细讲解一下“Jenkins+tomcat自动发布的热部署/重启及遇到的问题解决办法(推荐)”的完整攻略。 一、背景介绍 在我们的开发过程中,经常需要发布新的代码到服务器上。但是每次手动更新是十分繁琐的,而且还容易出错。因此我们需要一个自动化的过程来完成这个任务。Jenkins是目前最流行的自动化构建工具之一,它可以帮助我们实现自动化构建、测试、部署等任…

    Java 2023年5月20日
    00
  • 基于Gradle搭建Spring 5.3.13-release源码阅读环境的详细流程

    下面是基于Gradle搭建Spring 5.3.13-release源码阅读环境的详细流程: 环境准备 在开始之前,我们需要先准备好以下环境: JDK: 安装JDK 8及以上版本 Gradle:安装Gradle 6.8.3及以上版本 Git: 安装Git 2.23及以上版本 下载Spring源码 在完成环境准备之后,我们需要去Spring官网下载Spring…

    Java 2023年5月31日
    00
  • java如何判断一个数是否是素数(质数)

    判断一个数是否是素数是一个常见的算法问题,下面是用java编写的实现方法: 1.判断算法 判断一个数x是否为素数的方法是判断x是否能被2~sqrt(x)范围内的整数整除。如果有一个数能够整除x,那么x就不是素数,否则x就是素数。 示例代码: public static boolean isPrime(int x) { if (x < 2) { // 小…

    Java 2023年5月26日
    00
  • 如何基于SpringBoot部署外部Tomcat过程解析

    准备工作 在开始部署外部Tomcat之前,我们需要先准备好以下几点: 安装好Java环境,并配置好环境变量; 下载并解压Tomcat,建议下载Tomcat 9.x 版本; 新建一个Spring Boot项目,并配置好pom.xml文件,引入所需的相关依赖。 配置外部Tomcat与Spring Boot项目的关联 接下来,我们要将Spring Boot项目部署…

    Java 2023年6月2日
    00
  • 探究JavaScript函数式编程的乐趣

    探究JavaScript函数式编程的乐趣 函数式编程是一种以函数为基础,将计算看作数学函数的风格。这种编程方式通常被指定为声明式编程,因为它主要使用函数声明来刻画程序结果。本文将介绍JavaScript中的函数式编程的乐趣,并引入两个示例以解释其用途。 什么是函数式编程? 函数式编程是一种流行的JavaScript编程范式。它的目标是使用函数来处理数据,而不…

    Java 2023年5月26日
    00
  • 让Java程序自动重启的实现方法(推荐)

    让我们来详细讲解一下“让Java程序自动重启的实现方法(推荐)” 实现的完整攻略。 1. 监听文件变化方式 这种方式是通过文件监听来实现的,当指定的文件发生变化时,可以通过管道的方式向Java程序发送消息,让程序自动重启。 首先,可以在Java代码中通过第三方库jnotify来实现文件监听。以下是一个示例代码: // 引入jnotify依赖 <depe…

    Java 2023年5月23日
    00
  • java如何判断一个对象是否为空对象

    判断一个Java对象是否为空对象,通常可以通过以下几种方式进行: 1. 使用 == 进行判断 我们可以使用 Java 中的双等号 “==” 运算符来判断一个对象是否为 null。如果对象为 null,则其值为 null,否则就是一个有效对象。 下面是一个示例代码: Object object = null; if (object == null) { Sys…

    Java 2023年5月26日
    00
  • 使用Feign设置Token鉴权调用接口

    使用Feign进行Token鉴权调用接口,主要需要完成以下几个步骤: 在Feign客户端添加Token拦截器 在Feign接口定义处添加@RequestHeader注解,设置Token鉴权信息 下面分别详细讲解这两个步骤。 步骤一:在Feign客户端添加Token拦截器 Feign的Token拦截器需要实现RequestInterceptor接口,因此我们需…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部