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

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

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

目录
相关文章
|
6天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
24 2
|
27天前
|
自然语言处理 算法 安全
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
18 0
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-16
|
27天前
|
机器学习/深度学习 自然语言处理 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-12(上)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-12(上)
31 0
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-12(上)
|
1月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
61 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
27天前
|
机器学习/深度学习 安全 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(下)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(下)
34 0
|
27天前
|
安全 搜索推荐 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(上)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-23(上)
30 0
|
27天前
|
自然语言处理 搜索推荐 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-21(下)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-21(下)
31 0
|
27天前
|
机器学习/深度学习 人工智能 自然语言处理
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-21(上)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-21(上)
22 0
|
27天前
|
机器学习/深度学习 人工智能 算法
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20(下)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20(下)
20 0
|
27天前
|
机器学习/深度学习 存储 人工智能
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20(上)
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20(上)
20 0