【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 { |