代码随想录算法训练营 Day 37

1 minute read

Published:

动态规划五部曲

  1. 确定dp数组以及下标的含义
  2. 确定递推公式
  3. 初始化dp数组
  4. 确定遍历顺序
  5. 举例推导dp数组

第5步随着输入不同而变化,因此在此省去

Q1. LeetCode 300

前四步:

  1. 使用一维的dp数组,dp[i]表示以第i位数字结尾的最长递增子序列长度
  2. 递推公式:比较某个数字和前面的数字,如果比前面数字大,那么序列长度加1。因此需要两层循环,找到当前位置的最大值,即 $dp[i]=max(dp[i], dp[j] + 1)$
  3. 初始化:整体初始化为1,因为数字本身就是一个长度为1的序列
  4. 遍历顺序:两层循环均从前向后遍历
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1] * n

        for i in range(1, n):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
        
        return max(dp)

复杂度分析

时间复杂度:O($n^2$) 空间复杂度:O($n$)

Q2. LeetCode 674

和Q1类似,但因为现在要找的连续子数组,因此只需要比较当前位置和前一位置的大小

class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1] * n

        for i in range(1, n):
            if nums[i] > nums[i - 1]:
                dp[i] = dp[i - 1]+ 1
        
        return max(dp)

复杂度分析

时间复杂度:O($n$) 空间复杂度:O($n$)

Q3. LeetCode 718

前四步:

  1. 使用二维的dp数组,dp[i][j]表示nums1[:i+1]和nums2[:j+1]之间的最长重复数组的长度
  2. 递推公式:假如两个数组中存在 $nums1[i]$和 $nums2[j]$相同的情况,那么最长重复数组的长度可以表示为 $dp[i - 1][j - 1] + 1$
  3. 初始化:整体初始化为0,然后初始化第一行和第一列
  4. 遍历顺序:两层循环均从前向后遍历(从1开始)
class Solution:
    def findLength(self, nums1: List[int], nums2: List[int]) -> int:
        m, n = len(nums1), len(nums2)

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

        for i in range(m):
            if nums1[i] == nums2[0]:
                dp[i][0] = 1
        
        for j in range(n):
            if nums2[j] == nums1[0]:
                dp[0][j] = 1

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

复杂度分析

时间复杂度:O($m \times n$) 空间复杂度:O($m \times n$)