leetcode(6)子序列系列题目

动态规划:

子序列(不连续)

(1)300. 最长递增子序列

注意:可以不连续
dp[i]表示i之前包括i的以nums[i]结尾最长上升子序列的长度

状态转移方程:位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。
所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
即将dp[i]更新为dp[j] + 1的最大值。

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1] * len(nums)
        for i in range(len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

(2)1143. 最长公共子序列

1035. 不相交的线 与本题实质上是同一个题目
注意:与 718. 最长重复子数组 的区别在于这里不要求是连续的了,但要有相对顺序,即:"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
dp[i][j]:长度为[0, i - 1]的字符串text1长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]

  • 如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
  • 如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
    即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        len1, len2 = len(text1), len(text2)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        for i in range(1, len1 + 1):  # 记得+1!!!
            for j in range(1, len2 + 1):
                if text1[i - 1] == text2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                else:
                    dp[i][j] = max(dp[i][j - 1], dp[i - 1][j])
        return dp[-1][-1]

子数组(连续)

(3)674. 最长连续递增序列

注意:与 300. 最长递增子序列 的区别在于这个连续,不需要两个for循环

class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        res = 1
        n = len(nums)
        if n == 1:return 1
        dp = [1] * n
        for i in range(1, n):
            if nums[i] > nums[i - 1]:
                dp[i] = dp[i - 1] + 1
            res = max(res, dp[i])
        return res
(4)718. 最长重复子数组

dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。
当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;
注意:与 1143. 最长公共子序列 的区别在于A[i - 1] 和B[j - 1]不相等的时候,从0开始计算,因此不需要判断

class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        len1, len2 = len(nums1), len(nums2)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        res = 0
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if nums1[i - 1] == nums2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                res = max(res, dp[i][j])
        return res
(5)53. 最大子数组和

这道题目的思想是: 走完这一生 如果我和你在一起会变得更好,那我们就在一起,否则我就丢下你。 我回顾我最光辉的时刻就是和不同人在一起,变得更好的最长连续时刻

如果前边累加后还不如自己本身大,那就把前边的都扔掉,从此自己本身重新开始累加。
如果加入当前元素后的cur < 没有加之前的res,则不会添加当前元素

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        res = cur = nums[0]
        for i in range(1, len(nums)):
            cur += nums[i]
            cur = max(cur, nums[i])
            res = max(res, cur)
        return res

编辑距离

(6)392. 判断子序列

注意:与 1143. 最长公共子序列 的区别是这里是判断s是否为t的子序列,即t的长度是大于等于s的。
如果s[i - 1] 与 t[j - 1]不相同,只要看s[i - 1]与 t[j - 2]的比较结果,即:dp[i][j] = dp[i][j - 1];
编辑距离的入门题目

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        len1, len2 = len(s), len(t)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if s[i - 1] == t[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                else:
                    dp[i][j] = dp[i][j - 1]
        return dp[-1][-1] == len(s)
(7)115. 不同的子序列

注意:与 392. 判断子序列 的区别是这里是计算在 s 的子序列中 t 出现的个数,即s的长度是大于等于t的。
dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。

  • 当s[i - 1] 与 t[j - 1]相等时,dp[i][j]可以有两部分组成。

    • 一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。
    • 一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。

    所以dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];

  • 当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,即:dp[i][j] = dp[i - 1][j];

dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数。
那么dp[i][0]一定都是1,因为也就是把以i-1为结尾的s,删除所有元素,出现空字符串的个数就是1。

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        len1, len2 = len(s), len(t)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        for i in range(len1 + 1):
            dp[i][0] = 1
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if s[i - 1] == t[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
                else:
                    dp[i][j] = dp[i - 1][j]
        return dp[-1][-1]
(8)583. 两个字符串的删除操作

注意: 与 115.不同的子序列 的区别是两个字符串都可以删除
dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。

  • 当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];

  • 当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:

    • 情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
    • 情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
    • 情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2

    最后取最小值:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});

dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。dp[0][j]同理

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        len1, len2 = len(word1), len(word2)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        for i in range(len1 + 1):
            dp[i][0] = i
        for j in range(len2 + 1):
            dp[0][j] = j
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if word1[i - 1] == word2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1]
                else:
                    dp[i][j] = min(dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1)
        return dp[-1][-1]
(9)72. 编辑距离

注意: 与 115.不同的子序列 的区别是删除变成3种操作;与 583. 两个字符串的删除操作 的区别是只能对一个字符串,但是可以进行3种操作

  • 当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:

    • 情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
    • 情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1(word2添加一个元素,相当于word1删除一个元素
    • 情况三:替换操作,操作的最少次数为dp[i - 1][j - 1] + 1

    最后取最小值:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;

dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。

那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;dp[0][j]同理
2023.06.08百度文库一面

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        len1, len2 = len(word1), len(word2)
        dp = [[0] * (len2 + 1) for _ in range(len1 + 1)]
        for i in range(len1 + 1):
            dp[i][0] = i
        for j in range(len2 + 1):
            dp[0][j] = j
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if word1[i - 1] == word2[j - 1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
        return dp[-1][-1]
(10)44. 通配符匹配
class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        len1, len2 = len(s), len(p)
        dp = [[False] * (len2 + 1) for _ in range(len1 + 1)]
        dp[0][0] = True
        # s是空字符串时,p可以匹配的位置
        for j in range(1, len2 + 1):
            if p[j - 1] == '*':
                dp[0][j] = dp[0][j - 1]
            else:
                break
        for i in range(1, len1 + 1):
            for j in range(1, len2 + 1):
                if p[j - 1] == '*':
                    dp[i][j] = dp[i - 1][j] or dp[i][j - 1]
                elif p[j - 1] == '?' or p[j - 1] == s[i - 1]:
                    dp[i][j] = dp[i - 1][j - 1]
        return dp[-1][-1]

贪心算法:

(2)674. 最长连续递增序列

其实就是动态规划的dp数组改成一个变量来存

class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        res = cur = 1
        n = len(nums)
        if n == 1:return 1
        for i in range(1, n):
            if nums[i] > nums[i - 1]:
                cur += 1
            else:
                cur = 1
            res = max(res, cur)
        return res

(5)53. 最大子数组和

双指针:

(6)392. 判断子序列

参考资料:
动态规划之编辑距离总结篇

posted @ 2022-05-12 17:10  YTT77  阅读(69)  评论(0编辑  收藏  举报