代码随想录算法训练营day52 | 300.最长递增子序列,674. 最长连续递增序列,718. 最长重复子数组

300.最长递增子序列

五部曲:

1. dp[i]的定义:dp[i]表示i之前包括i的以nums[i]结尾最长上升子序列的长度

2. 确定递推公式:位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 +1 的最大值。

  • 为什么要+1?如果nums[j]也就是nums[i-1] > nums[i]的时候,这时候就不能+1?
  • 所以在取最大值之前要判断 if  (nums[i] > nums[j]),即 if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
  • 注意这里是要取dp[j] + 1的最大值

3. dp[i]的初始化:每一个i,对应的dp[i](即最长上升子序列)起始大小至少都是1

4. 确定遍历顺序:dp[i] 是有0到i-1各个位置的最长升序子序列 推导而来,那么遍历i一定是从前向后遍历。

5. 打印检查

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)
            
        dp = [1]*len(nums)
        res = 0
        for i in range(len(nums)):
            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. 最长连续递增序列

相较于#300,这道题是求最长连续递增序列,这样就排除了通过删除元素进而组成更长的递增子序列的情况(#300的情况)

五部曲:

  1. dp数组的含义:dp[i]:以下标i为结尾的数组的连续递增的子序列长度为dp[i]。
  2. 确定递推公式:如果 nums[i + 1] > nums[i],那么以 i+1 为结尾的数组的连续递增的子序列长度 一定等于 以i为结尾的数组的连续递增的子序列长度 + 1 。即 if nums[i + 1] > nums[i]: dp[i + 1] = dp[i] + 1;
  3. dp数组如何初始化:dp[i] = 1
  4. 确定遍历顺序:从递推公式上可以看出,dp[i + 1]依赖dp[i],所以是从前向后遍历。
  5. 打印检查
class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)
            
        dp = [1]*len(nums)
        res = 0
        for i in range(len(nums)-1):
            if nums[i+1] > nums[i]:
                dp[i+1] = dp[i] + 1
            res = max(res, dp[i+1])
        return res

718. 最长重复子数组

  • 注意题目中说的子数组,其实就是连续子序列!

五部曲:

  1. 确定dp数组以及下标的含义:dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。(特别注意: “以下标i - 1为结尾的A” 表明一定是 以A[i-1]为结尾的字符串 )
  2. 确定递推公式:
    1. 根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。
    2. 即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;(因为dp[i - 1][j - 1]以下标i - 2为结尾的A,和以下标j - 2为结尾的B的最长重复子数组长度,那么由于A[i - 1] == B[j - 1],因此就需要再 + 1)
    3. 根据递推公式可以看出,遍历i 和 j 要从1开始!
  3. dp数组如何初始化:
    1. 根据dp[i][j]的定义,dp[i][0] 和dp[0][j]其实都是没有意义的!
    2. 但dp[i][0] 和dp[0][j]要初始值,因为为了方便递归公式dp[i][j] = dp[i - 1][j - 1] + 1;

      所以dp[i][0] 和dp[0][j]初始化为0。

  4. 确定遍历顺序:从前向后遍历,先遍历A或者B都可以
  5. 打印检查
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        dp = [[0] * (len(nums2)+1) for _ in range(len(nums1)+1)]
        result = 0
        for i in range(1, len(nums1)+1):
            for j in range(1, len(nums2)+1):
                if nums1[i-1] == nums2[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                result = max(result, dp[i][j])
        return result
  • 因为dp数组的定义是关于i-1和j-1的,所以才需要长度为len()+1

  • (Trick:其实在定义数组的时候可以直接多加一个1,因为空间上不会影响多少,并且也避免了可能的错误,类似这道题按照定义就必须+1)

  • 可以看出dp[i][j]都是由dp[i - 1][j - 1]推出。那么压缩为一维数组,也就是dp[j]都是由dp[j - 1]推出。
  • 注意:此时遍历B数组(或者说内循环)的时候,就要从后向前遍历,(参考滚动数组遍历顺序)
class Solution(object):
    def findLength(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: int
        """
        dp = [0] * (len(nums2) + 1)
        result = 0
        for i in range(1, len(nums1)+1):
            for j in range(len(nums2), 0, -1):
                if nums1[i-1] == nums2[j-1]:
                    dp[j] = dp[j-1] + 1
                else:
                    dp[j] = 0 #注意这里不相等的时候要有赋0的操作
                result = max(result, dp[j])
        return result
  • 像#300和#674这两道题对于dp数组的定义就比较直观(以下标i为结尾的数组),但是这道题的定义要以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。(虽说carl也提到了将dp[i][j]定义成以下标i为结尾的A,和以下标j 为结尾的B,最长重复子数组长度也是可以的;但是比较复杂,具体如何复杂没有细讲,所以对于这道题的dp数组定义并不好想。)
  •  参考https://juejin.cn/post/7164509340458549278

  • 可以实现dp数组定义是以下标 为结尾的A,和以下标 为结尾的B,最长重复子数组长度为dp[i][j]。
  • 但需要initialized the first row and the first column
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        dp = [ [0]*(len(nums2)) for _ in range(len(nums1)) ]
        res = 0

        # initialized the first row and the first column
        for i in range(len(nums1)):
            if nums1[i] == nums2[0]:
                dp[i][0] = 1
                res = max(res, 1)
                
        for j in range(len(nums2)):
            if nums2[j] == nums1[0]:
                dp[0][j] = 1
                res = max(res, 1)

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

你可能感兴趣的:(动态规划,算法,数据结构,leetcode,python)