引言:

  今天分享的题目是 LeetCode 上的第 1143 题最长公共子序列,难度是中等。解题的思路是动态规划(Dynamic Programing)。
  动态规划的题解都是不好想到的,如果没有动态规划相关的的经验,基本上想不到这样的解题方法。我写这篇文章的意义,也就是将解这道题或者类似题目的动态规划的解题方法讲解清楚,为后续的发展打下基础。

1.最长公共子序列

题目描述

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

示例:

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


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

 

2.思路讲解

LeetCode 1143:初中级算法题解 - 动态规划_python

 

 

 

意义:
  如上图所示, dp(i, j)表示的意义:是texts1 前 i 个元素texts2 前 j 个元素的最长公共子序列长度。那么:

  • 首先dp(i, 0)、dp(0, j) 初始值均为 0。 理解:这个很好理解,只要有   text1和  text2有一个是空串,它们的公共子序列的长度就为0

  • 如果 texts1[i – 1] = texts2[j – 1],那么 dp(i, j) = dp(i – 1, j – 1) + 1理解:如果 texts1[i-1] = texts2[j-1],它们的公共子序列的长度就是在dp(i-1, j-1)上加1

  • 如果 texts1[i – 1] ≠ texts2[j – 1],那么 dp(i, j) = max { dp(i – 1, j), dp(i, j – 1) }理解:如果texts[i-1] != texts2[j-1],则其结果如下图所示的2种情况,我们只取最大的那个
  • 1.要么就是texts1 前 i-1 个元素texts2 前 j 个元素的最长公共子序列长度
  • 2.要么就是texts1 前 i 个元素texts2 前 j-1 个元素的最长公共子序列长度

LeetCode 1143:初中级算法题解 - 动态规划_动态规划_02

 

 

3.核心算法

dp的意义: dp(i, j) 是【nums1 前 i 个元素】与【nums2 前 j 个元素】的最长公共子序列长度

  • dp(i, 0)、dp(0, j) 初始值均为 0

  • 如果 nums1[i – 1] = nums2[j – 1],那么 dp(i, j) = dp(i – 1, j – 1) + 1

  • 如果 nums1[i – 1] ≠ nums2[j – 1],那么 dp(i, j) = max { dp(i – 1, j), dp(i, j – 1) }

m = tests1.count, n = tests2.count

  1. 一开始,我们会创建一个dp[m+1][n+1]的数组,即m+1n+1

  2. 然后使用上面的核心算法给数组的每一个值计算结果

  3. 我们最后取dp[m][n]

 

dp数组的计算结果如下图所示

【因为2层for循环,可以把矩阵的每个位置的数据都算出来,然后找到最大值】

LeetCode 1143:初中级算法题解 - 动态规划_python_03

 

 

图中数据颜色讲解:

  1. 黑色部分的0,是因为dp[i][0]dp[0][j]的值肯定为0,因此没有交集

  2. 橙色部分标记的意思是公共子序列的一员

  3. 红色标记了序号,灰色是texts1和texts2

 

4.代码实现

func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int {
    if text1.count == 0 || text2.count == 0 { return 0 }
    // 将字符串转成数组
    var _text1 = Array(text1)
    var _text2 = Array(text2)
    // 创建 [m+1][n+1]的数组
    var dp = [[Int]](repeating: [Int](repeating: 0, count: text2.count+1), count: text1.count+1)
    for i in 1...text1.count {
        for j in 1...text2.count {
            // 这句是重点 [i-1]和[j-1],因为dp数组是m+1和n+1的,所以dp是可以从1开始的,但是texts数组是m的,是以0开始的。
            if _text1[i-1] == _text2[j-1]
            {
                dp[i][j] = dp[i-1][j-1] + 1
            }else {
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
            }
        }
    }
    // 2层for循环,会将数组里面的每个地方的值都算出来
    return dp[text1.count][text2.count]
}

总结

  今天比较详细的讲解了一道关于动态规划的问题。关于这种定义二维数组来解题的方式,其实在很多地方都会用到,希望这种思路,以及里面的实现细节能给大家带来启发。

欢迎关注【无量测试之道】公众号,
Python编程学习资源干货、
Python+Appium框架APP的UI自动化、
Python+Selenium框架Web的UI自动化、
Python+Unittest框架API自动化、
文章下方有公众号二维码,可直接微信扫一扫关注即可。

LeetCode 1143:初中级算法题解 - 动态规划_python_04