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 记录当前问题的状态和解,用通用公式进行状态转移,从而得出最优解。

阅读剩余 68%

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

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

相关文章

  • 浅谈Java开发中的安全编码问题

    浅谈Java开发中的安全编码问题 在Java开发中,安全编码是一个至关重要的问题。由于Java的开放性,其程序可运行于任何平台上,并且可以动态地加载类文件和执行代码,这意味着Java程序容易被黑客攻击。因此,在设计、编写和部署Java应用程序时必须考虑安全性,以保护用户数据和应用程序的稳定性。 常见安全编码问题 以下是Java开发中常遇到的一些安全编码问题:…

    Java 2023年5月20日
    00
  • java8中的lambda表达式简介

    首先我们来介绍一下Java 8中的Lambda表达式。Lambda表达式是一种新的语言特性,也是Java 8引入的最为重要的新特性之一。它简化了代码编写的难度,可以使代码更加简洁、易读。在Java 8之前,“匿名内部类”是开发人员实现不同功能所必须使用的主要方式。但是,使用匿名内部类的语法造成了很多冗余的代码,让代码可读性下降,而使用Lambda表达式可以使…

    Java 2023年5月26日
    00
  • JPA的多表复杂查询的方法示例

    JPA是Java Persistence API的缩写,它是Java EE中的一个API,提供了Java对象到关系数据库表之间的映射(ORM)功能。JPA中的多表复杂查询是指需要查询多个关联表的查询操作。下面将介绍JPA的多表复杂查询的方法示例。 一、JPA多表查询基本操作 定义多表查询的类 在JPA中,可以定义一个类来封装多表查询的结果,该类中包含了所有需…

    Java 2023年5月20日
    00
  • 4个Java8中你需要知道的函数式接口分享

    4个Java8中你需要知道的函数式接口分享 本文将介绍Java 8中比较有用的函数式接口。我们将会探究这些接口能够如何使用,以及有哪些主要的特点和优点。 1. Consumer接口 Consumer是一个消费者接口,它接受一个参数,但是没有返回值。在Java 8中,它被定义为一个通用的函数式接口。我们可以使用它来调用一个表示一些操作的代码块,而不需要在代码的…

    Java 2023年5月26日
    00
  • IDEA解决Java:程序包xxxx不存在的问题

    当我们在使用IntelliJ IDEA编写Java程序时,经常会遇到程序包不存在的问题,出现这种问题的原因是因为程序没有引用依赖库或依赖库的路径配置不正确。在这里,我们提供一些方法来解决这个问题。 方法一:在项目中添加依赖库 要在项目中添加依赖库,请使用以下步骤: 打开IntelliJ IDEA并打开你的项目。 在左侧的Project面板中,右键单击“Dep…

    Java 2023年5月19日
    00
  • Springboot如何实现自定义异常数据

    自定义异常类 首先,我们需要定义一个自定义异常类,用来处理我们所需要抛出的异常情况。该自定义异常类需要继承RuntimeException或其子类,如IllegalArgumentException等。在自定义异常类中,我们可以添加一些额外的信息字段,以方便我们在异常处理时获取更加详细的异常信息。 下面是一个自定义异常类的示例代码: public class…

    Java 2023年5月27日
    00
  • Spring Security前后分离校验token的实现方法

    我会详细讲解“Spring Security前后分离校验token的实现方法”的完整攻略。这里将分为以下几个步骤: 获得token 将token保存到请求头中 在后端进行token校验 返回结果给前端 下面我们具体来看一下每一步的实现方法。 1. 获得token 首先,我们需要在前端登录成功之后,获得token。我们可以通过发送登录请求来获取token,例如…

    Java 2023年5月20日
    00
  • 关于spring data jpa 模糊查询like的坑点

    好的。首先让我们讨论一下”关于Spring Data JPA模糊查询Like的坑点”的具体情况。 什么是Spring Data JPA模糊查询Like的坑点? 如果我们想使用Spring Data JPA执行模糊查询(例如使用LIKE操作符),我们需要注意一些事项。这些主要涉及到通配符的使用和查询条件的拼接。 通配符的使用 在使用LIKE操作符时,我们需要使…

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