Leetcode 动态规划 3

题号直达: 300 最长递增子序列 | 152 乘积最大子数组 | 416 分割等和子集 | 32 最长有效括号

LeetCode 300. 最长递增子序列 ↑

题目

给定整数数组 nums,返回其中严格递增子序列的最长长度。

子序列:由数组删除若干元素(可删 0 个)得到、且不改变剩余元素相对顺序的序列。
严格递增:序列中后项必须大于前项。

测试样例

  • 示例 1: nums = [10,9,2,5,3,7,101,18]
    输出:4
    解释:例如 [2,3,7,101],长度为 4。
  • 示例 2: nums = [0,1,0,3,2,3]
    输出:4
  • 示例 3: nums = [7,7,7,7,7,7,7,7]
    输出:1

提示

  • 1 <= nums.length <= 2500
  • -10^4 <= nums[i] <= 10^4

进阶:能否将时间复杂度降到 O(n log n)?(可用二分 /「耐心排序」思路,本题代码为 O(n²)。)

代码(Python)

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp = [0] * len(nums)
        dp[0] = 1
        for i in range(1, len(nums)):
            dp[i] = 1
            first = -float('inf')
            flag = 0
            for j in range(i):
                if nums[j] < nums[i] and dp[j] > first:
                    first = dp[j]
                    flag = 1  # 表示存在更小的合法前驱,非「排头」单独成列
            if flag:
                dp[i] = first + 1
        return max(dp)

思路解析

dp[i] 表示以 nums[i] 结尾的最长严格递增子序列长度。初始化 dp[i] = 1(仅含自身)。

对所有 j < i 且 nums[j] < nums[i] 的位置,可从 j 结尾的最优长度转移:
dp[i] = max(dp[j] + 1)。实现中用 first 滚动维护当前最大的 dp[j],flag 标记是否至少有一个合法 j(若没有则保持长度为 1)。

答案为 max(dp)。

  • 时间复杂度:O(n²),n = len(nums)。
  • 空间复杂度:O(n)。
  • 进阶:维护有序数组 + 二分可将更新降为 O(log n) 每次,总体 O(n log n)。

LeetCode 152. 乘积最大子数组 ↑

题目

给定整数数组 nums,找出数组中非空连续子数组的乘积最大值,返回该乘积。

  • 只含一个元素的子数组,乘积即为该元素本身。
  • 题目保证测试用例的答案在 32 位有符号整数范围内;任意子数组的乘积也可视为在 32 位整数范围内。

测试样例

  • 示例 1: nums = [2, 3, -2, 4]
    输出:6
    解释:子数组 [2, 3] 的乘积最大,为 6。
  • 示例 2: nums = [-2, 0, -1]
    输出:0
    解释:[-2, -1] 不是连续子数组,不能一起乘出 2。

提示

  • 1 <= nums.length <= 2 * 10^4
  • -10 <= nums[i] <= 10
  • 任意子数组乘积保证在 32 位整数范围内

代码(Python)

class Solution(object):
    def maxProduct(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        max_dp = [0] * len(nums)
        min_dp = [0] * len(nums)
        max_dp[0] = nums[0]
        min_dp[0] = nums[0]
        for i in range(1, len(nums)):
            max_dp[i] = max(
                max_dp[i - 1] * nums[i],
                min_dp[i - 1] * nums[i],
                nums[i],
            )
            min_dp[i] = min(
                max_dp[i - 1] * nums[i],
                min_dp[i - 1] * nums[i],
                nums[i],
            )
        return max(max_dp)

思路解析(如图示递推)

乘法与加法不同:负负得正,区间最大乘积未必来自「上一步最大 × 当前数」,也可能来自上一步最小(很负)× 当前负数。因此要同时维护两个量:

  • max_dp[i]:以 nums[i] 结尾的连续子数组的最大乘积。
  • min_dp[i]:以 nums[i] 结尾的连续子数组的最小乘积(最负)。

每一步在三种候选里取值:接上 i-1 的最大链、接上 i-1 的最小链、从 i 重新开始(仅 nums[i])。f_max、f_min 与代码中 max_dp、min_dp 的对应关系见下图。

乘积最大子数组:最大/最小乘积递推示意

全局答案为 max(max_dp)。

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n);可滚动为 O(1)。

LeetCode 416. 分割等和子集 ↑

题目

给定一个只包含正整数的非空数组 nums,判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

测试样例

  • 示例 1: nums = [1, 5, 11, 5]
    输出:true
    解释:可分为 [1, 5, 5] 与 [11],和均为 11。
  • 示例 2: nums = [1, 2, 3, 5]
    输出:false

提示

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

代码(Python)

class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        if len(nums) < 2:
            return False
        summary = sum(nums)
        if summary % 2 == 1:
            return False
        else:
            target = summary // 2
        maxNum = max(nums)
        if maxNum > target:
            return False
        dp = [[False for _ in range(target + 1)] for _ in range(len(nums))]
        for i in range(len(dp)):
            dp[i][0] = True
        dp[0][nums[0]] = True
        for i in range(1, len(nums)):
            for j in range(1, target + 1):
                if j >= nums[i]:
                    dp[i][j] = (dp[i - 1][j]) or dp[i - 1][j - nums[i]]
                else:
                    dp[i][j] = dp[i - 1][j]
        return dp[len(nums) - 1][target]

思路解析

分割等和子集 解析 1 分割等和子集 解析 2

LeetCode 32. 最长有效括号 ↑

题目

给你一个只包含 '(' 与 ')' 的字符串 s,找出最长的、合法且连续的括号子串的长度。

合法指括号能按顺序配对、形如平衡括号串(例如 "(())" 合法)。

测试样例

  • 示例 1: s = "(()"
    输出:2
    解释:最长合法子串为 "()"。
  • 示例 2: s = ")()())"
    输出:4
    解释:最长合法子串为 "()()"。
  • 示例 3: s = ""
    输出:0

提示

  • 0 <= s.length <= 3 * 10^4
  • s[i] 为 '(' 或 ')'

代码(Python)

class Solution(object):
    def longestValidParentheses(self, s):
        """
        :type s: str
        :rtype: int
        """
        dp = [0] * len(s)
        for i in range(len(s)):
            if s[i] == '(':
                dp[i] = 0

            # 发现右括号
            else:

                # 处理前期数组越界情况
                if i == 0:
                    dp[i] = 0
                elif i == 1:
                    if s[0] == '(':
                        dp[i] = 2
                    else:
                        dp[i] = 0
                else:
                    if s[i - 1] == '(':
                        dp[i] = dp[i - 2] + 2
                    else:
                        index = i - dp[i - 1] - 1
                        if index < 0:
                            dp[i] = 0
                        else:
                            if s[index] == '(':
                                if index - 1 >= 0:
                                    dp[i] = dp[i - 1] + dp[index - 1] + 2
                                else:
                                    dp[i] = dp[i - 1] + 2
                            else:
                                dp[i] = 0
        if dp:
            return max(dp)
        else:
            return 0

思路解析

最长有效括号 图解

posted @ 2026-10-12 00:33  Phier  阅读(2)  评论(0)    收藏  举报