《趣学算法-动态规划-最长的公共子序列》阅读笔记

简介: 《趣学算法-动态规划-最长的公共子序列》阅读笔记

14天阅读挑战赛

算法知识点

最长公共子序列(LCS)是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题。这与查找最长公共子串的问题不同的地方是:子序列不需要在原序列中占用连续的位置。而最长公共子串(要求连续)和最长公共子序列是不同的。

算法题目来源

LeetCode 1143. 最长公共子序列

算法题目描述

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

1 <= text1.length, text2.length <= 1000
text1 和 text2 仅由小写英文字符组成。

做题思路

  • 采用dp二维数组,dpi用来表示text1[i]与text2[j]的最长公共子序列长度
  • 遍历过程中,如果text1[i]与text2[j]相等时,则dpi = dpi - 1 + 1
  • 遍历过程中,如果text1[i]与text2[j]不相等时,,那么dpi = Math.max(dpi,dpi - 1)
  • 直到遍历dp数组完成,则dptext1.length()的值,即为我们所求的text1[i]与text2[j]的最长公共子序列长度

Java代码

class Main {
    public static void main(String[] args) {
        String str1 = "abcde";
        String str2 = "ace";
        System.out.println("两个字符串的最长公共子序列长度为:" + longestCommonSubsequence(str1, str2));
    }

    /**
     * 最长公共子序列
     * 
     * @Title: lcsSubString
     * @Description:
     * @author: itbird
     * @date 2022年10月21日 下午4:05:01
     * @param str1
     * @param str2
     * @return int 时间复杂度: O(m*n) 空间复杂度: O(m*n)
     */
    public static int longestCommonSubsequence(String text1, String text2) {
        if (text1 == null || text1.length() <= 0 || text2 == null
                || text2.length() <= 0) {
            return 0;
        }
        // dp二维数组,dp[i][j]用来表示text1[i]与text2[j]的最长公共子序列长度
        int[][] dp = new int[text1.length() + 1][text2.length() + 1];
        for (int i = 1; i < dp.length; i++) {
            for (int j = 1; j < dp[0].length; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    // 如果text1[i]与text2[j]不相等时,
                    // 那么dp[i][j] = Math.max(dp[i][j - 1],dp[i - 1][j])
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }
        return dp[text1.length()][text2.length()];
    }

}

问题扩展

如果此时要求输出最长公共子序列的具体字符串呢?有什么办法吗?
我们可以在上面的步骤上,进行如下修改:

  • 新增ci二维数组,保持过程状态,保留三个状态
  • 1 代表是dpi = dpi - 1 + 1,也就是来源于左上角
  • 2 代表是dpi = dpi,也就是来源于左边
  • 3 代表是dpi = dpi - 1,也就是来源于上边
  • 根据状态为,找到1状态的字符,拼接即为结果
class Main {
    public static void main(String[] args) {
        String str1 = "abcde";
        String str2 = "ace";
        System.out.println(
                "两个字符串的最长公共子序列长度为:" + longestCommonSubsequence(str1, str2));
    }

    /**
     * 最长公共子序列
     * 
     * @Title: lcsSubString
     * @Description:
     * @author: itbird
     * @date 2022年10月21日 下午4:05:01
     * @param str1
     * @param str2
     * @return int 时间复杂度: O(m*n) 空间复杂度: O(m*n)
     */
    public static int longestCommonSubsequence(String text1, String text2) {
        if (text1 == null || text1.length() <= 0 || text2 == null
                || text2.length() <= 0) {
            return 0;
        }
        // dp二维数组,dp[i][j]用来表示text1[i]与text2[j]的最长公共子序列长度
        int[][] dp = new int[text1.length() + 1][text2.length() + 1];
        int[][] cp = new int[text1.length() + 1][text2.length() + 1];

        for (int i = 1; i < dp.length; i++) {
            for (int j = 1; j < dp[0].length; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    cp[i][j] = 1;
                } else {
                    // 如果text1[i]与text2[j]不相等时,
                    // 那么dp[i][j] = Math.max(dp[i][j - 1],dp[i - 1][j])
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                    if (dp[i][j - 1] > dp[i - 1][j]) {
                        cp[i][j] = 2;
                    } else {
                        cp[i][j] = 3;
                    }
                }
            }
        }

        /**
         * 遍历,找到公共子序列字符串
         */
        for (int i = 0; i < cp.length; i++) {
            for (int j = 0; j < cp[0].length; j++) {
                if (cp[i][j] == 1) {
                    System.out.println(text1.charAt(i - 1));
                }
            }
        }
        return dp[text1.length()][text2.length()];
    }

}

运行结果:
在这里插入图片描述

相关算法题型题目总结

剑指 Offer II 095. 最长公共子序列

目录
相关文章
|
18天前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
231 1
|
9天前
|
传感器 资源调度 算法
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
本文提出一种多子带相干累积(MSCA)算法,通过引入空带和子带相干处理,解决DDMA-MIMO雷达的多普勒模糊与能量分散问题。该方法在低信噪比下显著提升检测性能,实测验证可有效恢复目标速度,适用于车载雷达高精度感知。
66 4
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
|
8月前
|
存储 算法 Java
算法系列之动态规划
动态规划(Dynamic Programming,简称DP)是一种用于解决复杂问题的算法设计技术。它通过将问题分解为更小的子问题,并存储这些子问题的解来避免重复计算,从而提高算法的效率。
268 4
算法系列之动态规划
|
9月前
|
算法 Java C++
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
221 5
|
8月前
|
算法 安全 调度
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
|
8月前
|
机器学习/深度学习 算法 测试技术
【动态规划篇】01 背包的逆袭:如何用算法装满你的 “财富背包”
【动态规划篇】01 背包的逆袭:如何用算法装满你的 “财富背包”
|
11月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
205 2
|
12月前
|
自然语言处理 算法 安全
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
96 0
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
|
12月前
|
机器学习/深度学习 安全 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(下)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(下)
157 0
|
12月前
|
安全 搜索推荐 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(上)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(上)
131 0

热门文章

最新文章