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

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

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. 最长公共子序列

目录
相关文章
|
2月前
|
算法 开发者 Python
惊呆了!Python算法设计与分析,分治法、贪心、动态规划...这些你都会了吗?不会?那还不快来学!
【7月更文挑战第10天】探索编程巅峰,算法至关重要。Python以其易读性成为学习算法的首选。分治法,如归并排序,将大问题拆解;贪心算法,如找零问题,每步求局部最优;动态规划,如斐波那契数列,利用子问题解。通过示例代码,理解并掌握这些算法,提升编程技能,面对挑战更加从容。动手实践,体验算法的神奇力量吧!
60 8
|
2月前
|
算法 Python
算法不再难!Python分治法、贪心、动态规划实战解析,轻松应对各种算法挑战!
【7月更文挑战第8天】掌握Python算法三剑客:分治、贪心、动态规划。分治如归并排序,将大问题拆解递归解决;贪心策略在每步选最优解,如高效找零;动态规划利用子问题解,避免重复计算,解决最长公共子序列问题。实例展示,助你轻松驾驭算法!**
50 3
|
1月前
|
机器学习/深度学习 算法 Java
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
这篇文章介绍了基于贪婪技术思想的Prim算法和Dijkstra算法,包括它们的伪代码描述、Java源代码实现、时间效率分析,并展示了算法的测试用例结果,使读者对贪婪技术及其应用有了更深入的理解。
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
|
1月前
|
算法 Java 测试技术
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
这篇文章介绍了基于动态规划法的三种算法:解决背包问题的递归和自底向上实现、Warshall算法和Floyd算法,并提供了它们的伪代码、Java源代码实现以及时间效率分析。
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
|
2月前
|
算法 Python
Python算法高手进阶指南:分治法、贪心算法、动态规划,掌握它们,算法难题迎刃而解!
【7月更文挑战第10天】探索Python算法的精华:分治法(如归并排序)、贪心策略(如找零钱问题)和动态规划(解复杂问题)。通过示例代码揭示它们如何优化问题解决,提升编程技能。掌握这些策略,攀登技术巅峰。
62 2
|
2月前
|
算法 程序员 Python
算法小白到大神的蜕变之路:Python分治法、贪心、动态规划,一步步带你走向算法巅峰!
【7月更文挑战第9天】探索算法之旅,以Python解锁编程高手之路。分治法如二分查找,将复杂问题拆解;贪心算法解决活动选择,每次选取局部最优;动态规划求斐波那契数列,避免重复计算,实现全局最优。每一步学习,都是编程能力的升华,助你应对复杂挑战,迈向算法大师!
34 1
|
2月前
|
存储 算法 Python
Python算法界的秘密武器:分治法巧解难题,贪心算法快速决策,动态规划优化未来!
【7月更文挑战第9天】Python中的分治、贪心和动态规划是三大关键算法。分治法将大问题分解为小问题求解,如归并排序;贪心算法每步选局部最优解,不保证全局最优,如找零钱;动态规划存储子问题解求全局最优,如斐波那契数列。选择合适算法能提升编程效率。
49 1
|
2月前
|
存储 算法 Python
震撼!Python算法设计与分析,分治法、贪心、动态规划...这些经典算法如何改变你的编程世界!
【7月更文挑战第9天】在Python的算法天地,分治、贪心、动态规划三巨头揭示了解题的智慧。分治如归并排序,将大问题拆解为小部分解决;贪心算法以局部最优求全局,如Prim的最小生成树;动态规划通过存储子问题解避免重复计算,如斐波那契数列。掌握这些,将重塑你的编程思维,点亮技术之路。
51 1
|
2月前
|
存储 算法 大数据
Python算法高手的必修课:深入理解分治法、贪心算法、动态规划,让你的代码更智能!
【7月更文挑战第9天】在Python算法学习中,分治法(如归并排序)将大问题分解为小部分递归解决;贪心算法(如货币找零)在每步选择局部最优解尝试达到全局最优;动态规划(如斐波那契数列)通过存储子问题解避免重复计算,解决重叠子问题。掌握这三种方法能提升代码效率,解决复杂问题。
35 1
|
2月前
|
算法 索引 Python
逆袭算法界!Python分治法、贪心算法、动态规划深度剖析,带你走出算法迷宫!
【7月更文挑战第8天】分治法,如快速排序,将大问题分解并合并解;贪心算法,选择局部最优解,如活动选择;动态规划,利用最优子结构避免重复计算,如斐波那契数列。Python示例展示这些算法如何解决实际问题,助你精通算法,勇闯迷宫。
30 1