【leetcode】1143. 最长公共子序列
leetcode 刷题笔记 - 1143. 最长公共子序列
题目
https://leetcode.cn/problems/longest-common-subsequence/description/
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,”ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
1 | 示例 1: |
提示
1 | 1 <= text1.length, text2.length <= 1000 |
思路
这道题和 718. 最长重复子数组的区别在于,718 要求的是连续的子序列,而本题不要求子序列连续,就不是让你算子数组了。
虽然题目给出的是字符串,但本质上我们可以把它当作数组来处理,没有区别。
dp[i][j]
代表字符串 a 中i-1
和字符串 b 中j-1
下标之前的最长公共子序列的长度。
这样做就可以不对数组提前进行初始化了,因为 dp[0][x]
和 dp[x][0]
都是没有意义的。因为不会存在以下标 -1
为结尾的字符串,也自然没有公共子序列。换句话说,长度为 0 的字符串是不会有公共子序列的。
依照 dp 数组,可以想出递推的公式,当 a[i-1]
和 b[j-1]
相同的时候,就说明子序列可以被扩展,dp[i][j] == dp[i-1][j-1]+1
。
如果不相同,则代表子序列断了,但注意我们 dp 数组的含义,它代表的是 i-1 和 j-1 之前的最长公共子序列的长度,即便 i-1 和 j-1 不相等,这个最长公共子序列依旧是有一个取值的,即选用 “前一位” 的公共子序列长度最大值。
但由于 dp 是一个二维数组,这里的 “前一位” 就有两个情况了,分别是 dp[i-1][j]
和 dp[i][j-1]
,对应含义是字符串 a 中前一位(根据 dp 数组,下标是 i-1,公共子序列的含义是 i-2)和 b[j-1]
能构成的最长公共子序列,以及字符串 b 中前一位和 a[i-1]
能构成的最长公共子序列的长度。需要用 max 来选取二者的最大值。
递推的代码如下。
1 | if (a[i - 1] == b[j - 1]) { |
根据递推公式,dp[i][j]
的值可以从 dp 数组中的三个方向推导出来,如下图所示。
剩下的代码就很简单了,因为我们不需要对 dp 数组进行提前初始化,直接用 vector 构造函数统一初始化为 0 就可以了,然后从 1 开始遍历直到字符串末尾,维护一个最大值,返回即可。
其实不维护最大值也是可以的,因为根据 dp 数组的定义,dp[a.size()][b.size()]
就是我们需要的最大值。
代码 1
完整的代码如下,具体参考注释中的说明。
1 | class Solution { |
代码 2:提前初始化
依照 718 题的思路,我们也可以写出一个提前初始化 dp 数组的代码,完整代码如下。
注意初始化的情况也有所不同,因为本题要求的是子序列,可以出现不连续的情况,即便 a[i] != b[0]
,我们也需要沿用之前 dp[i - 1][0]
的结果,来保证初始化是正确的。
1 | class Solution { |
- 最新
- 最热
- 最早
- 作者
点击重新获取 | 打开控制台