day43-44 动态规划 子序列问题-上

114 阅读5分钟

第三天坚持,加油啊!

300. 最长递增子序列

文章讲解

思路:

思路: 最长递增子序列,dp[i]表示以nums[i]为结尾的最长递增子序列的长度。
初始化:1,每个元素最短是以自身为子序列,长度为1
递推:对于每一个元素dp[i],需要找到j∈[0,...,i-1]中nums[j] < nums[i]的dp[j],然后取max(dp[i], dp[j]+1)
递推方向:从左往右
最终答案是dp中最大的
class Solution(object):
    def lengthOfLIS(self, nums):
        n = len(nums)
        # if n == 1: return 1
        # dp初始化
        dp = [1] * n

        res = 0
        for i in range(n): # 注意,如果1-n,需要判断n=1的base case返回1
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j]+1)
            res = max(res, dp[i])
        return res

674. 最长连续递增序列

文章讲解

此题需要连续的递增子序列最大长度,不能删除元素。

思路:

想上去和第一题类似,之前是通过和0~i-1的dp[j]比较,完成递增子序列。如果要连续的子序列,那是不是最多和前面一次比较。
class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 1: return 1

        dp = [1] * n
        res = 0
        for i in range(1, n):
            if nums[i-1] < nums[i]:
                # dp[i] = max(dp[i], dp[i-1]+1)
                dp[i] = dp[i-1] + 1
            res = max(res, dp[i])
        return res

718. 最长重复子数组

文章讲解

需要找到两个数组中公共的最长子数组,子数组区别于子序列,子数组是必须要连续的。

思路:

本题是二维的dp。
定义:dp[i][j]表示nums1[0,..,i]和nums[0,..,j]的最长公共子数组,最终返回dp[m][n], dp:(m+1)*(n+1)
初始化:一边是空数组时候,公共长度为0
~~递推:尝试和左边、上边,左上角看看有没有递推关系,~~


看了代码随香录的思路:
和最长公共子序列LCS类似,需要二维的dp来递推,并且子数组是连续的,dp[i][j]只能通过dp[i-1][j-1]递推得到
定义:dp[i][j]表示s1[0:i-1]和s2[0:j-1]两个子数组的最大公共子数组长度,其中两个子数组长度分别是i和j,例如dp[2][4]=2表示ad adec两个子数组的最大公共子数组长度
初始化:任意一边空数组时候,dp=0
递推:子数组问题是连续的,只和前一个有关,当nums[i-1]和nums2[i-1]相等时候,dp[i][j] = dp[i-1][j-1] + 1
递推方向:左上到右下,先迭代nums1再迭代nums2
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        m, n = len(nums1), len(nums2)

        dp = [[0] * (n+1) for _ in range(m+1)]
        res = 0
        for i in range(1, m+1):
            for j in range(1, n+1):
                # dp[i][j]是s1[0...i-1] s2[0...j-1],如果末尾相等,那就可以从dp[i-1][j-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

1143. 最长公共子序列

文章讲解

思路:

定义:dp[i][j]表示s1[0..i-1]和s2[0..j-1]的最长公共子序列长度,
初始化:
递推:如果最后的s1[i-1]==s2[j-1],那么dp[i][j] = dp[i-1][j-1]+1;如果不相同,取max(dp[i-1][j], dp[i][j-1])
class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        m, n = len(text1), len(text2)
        dp = [[0] * (n + 1) for _ in range(m+1)]

        res = 0
        for i in range(1, m+1):
            for j in range(1, n+1):
                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])
                res = max(res, dp[i][j])
        return res

1035. 不相交的线

文章讲解

思路:

要找两个序列相同的数字,不相交就是顺序画,实际在做最长公共子序列。

class Solution:
    def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
        m, n = len(nums1), len(nums2)

        dp = [[0] * (n+1) for _ in range(m+1)]

        res = 0
        for i in range(1, m+1):
            for j in range(1, n+1):
                if nums1[i-1] == nums2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])
                res = max(res, dp[i][j])
        return res

53. 最大子数组和

文章讲解

思路:

求连续的子数组的最大和。
定义:dp[i]表示以i结尾的子数组的最大和。
递推:dp[i] = max(nums[i], dp[i-1] + nums[i]) # 或者从当前元素开始,或者之前的加上现在的
初始化:dp[0] = nums[0]
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if not nums:
            return 0
        
        n = len(nums)
        # 初始化
        dp = [0] * n
        dp[0] = nums[0] 

        # res =0
        res = dp[0] # 注意,res不能取0!
        for i in range(1, n):
            dp[i] = max(dp[i-1] + nums[i], nums[i])
            res = max(res, dp[i])
        return res

392. 判断子序列

文章讲解

思路1:双指针

双指针,迭代t,遇到和s相等的字符,s的指针也前进1
class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        m, n = len(s), len(t)
        i = j = 0

        while i < m and j < n:
            if s[i] == t[j]:
                i += 1
            j += 1
        return i == m

思路2: 动态规划

动态规划,参考最长公共子序列,dp[i][j]表示s[0..i-1]和t[0..j-1]的最长公共子序列。如果s[i-1]==t[j-1],那么长度+1;如果不相等,由于t>s,那么需要t-1,即j-2,也就是dp[i][j] = dp[i][j-1]
class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        m, n = len(s) , len(t)

        dp = [[0] * (n+1) for _ in range(m+1)]

        for i in range(1, m+1):
            for j in range(1, n+1):
                if s[i-1] == t[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1 # 出现公共字符,在dp[i-1][j-1]基础上+1
                else:
                    dp[i][j] = dp[i][j-1] # 不相同,需要从t中删除字符
        
        return dp[m][n] == m # 如果最长公共子序列长度为m就返回true