Skip to content

勇闯秋招手撕系列 V:动态规划 2️⃣

约 1165 个字 193 行代码 预计阅读时间 6 分钟 总阅读量

爬楼梯系列🌟

单词拆分

不同路径系列🌟

最小路径和

最大正方形🌟🌟

交错字符串(150)

买卖股票的最佳时机系列(150)🌟

最长有效括号

70. 爬楼梯 I

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?输入:n = 2;输出:2

class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        if n == 2:
            return 2 
        dp = [0 for _ in range(n + 1)]
        dp[0] = 1
        dp[1] = 1
        for i in range(2, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]
        return dp[-1]

746. 使用最小费用爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        n = len(cost)
        dp = [0] * n 
        dp[0] = cost[0]
        dp[1] = cost[1]
        for i in range(2, n):
            dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
        return min(dp[-1], dp[-2])

139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        self.res = False 
        maxLen = max(map(len, wordDict))
        wordSet = set(wordDict)
        n = len(s)
        @cache
        def dfs(idx):
            if idx == n:
                self.res = True 
                return 

            for i in range(maxLen):
                if s[idx: idx + i + 1] in wordSet:
                    dfs(idx + i + 1)
        dfs(0)
        return self.res

221. 最大正方形

在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        m = len(matrix)
        n = len(matrix[0])
        dp = [[0 for _ in range(n)] for _ in range(m)]
        res = 0
        for i in range(m):
            if matrix[i][0] == "1":
                dp[i][0] = 1
                res = 1
        for i in range(n):
            if matrix[0][i] == "1":
                dp[0][i] = 1
                res = 1
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == "1":
                    w = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
                    dp[i][j] = w 
                    res = max(res, w * w )
        return res

64. 最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m = len(grid)
        n = len(grid[0])
        dp = [[0 for _ in range(n)] for _ in range(m)]
        dp[0][0] = grid[0][0]
        for i in range(1, m):
            dp[i][0] = dp[i - 1][0] + grid[i][0]
        for i in range(1, n):
            dp[0][i] = dp[0][i - 1] + grid[0][i]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
        return dp[-1][-1]

63. 不同路径

给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。返回机器人能够到达右下角的不同路径数量。

class Solution:
    def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int:
        m = len(grid)
        n = len(grid[0]) 
        dp = [[0 for _ in range(n)] for _ in range(m)]
        dp[0][0] = 1
        if grid[0][0] == 1 or grid[-1][-1] == 1:
            return 0
        for i in range(1, m):
            if grid[i][0] == 1:
                break
            else:
                dp[i][0] = 1
        for i in range(1, n):
            if grid[0][i] == 1:
                break
            else:
                dp[0][i] = 1
        for i in range(1, m):
            for j in range(1, n):
                if grid[i][j] == 1:
                    dp[i][j] = 0
                else:
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        return dp[-1][-1]

32. 最长有效括号

难度:Medium 困难 。给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。输入:s = "(()";输出:2

from collections import deque
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        stack = deque([-1])
        res = 0
        for idx, sub_s in enumerate(s):
            if sub_s == "(":
                stack.append(idx)
            else:
                stack.pop()
                if len(stack) == 0:
                    stack.append(idx)
                else:
                    res = max(res, idx - stack[-1])
        return res

每次标记“前一个导致不可行的位置,注意前面补-1;

如果遇到了一个 (,无脑入stack;

如果遇到了一个 ),默认踢掉前面一个(假设进行了匹配),这时候有一种情况是把原先队列的 -1 踢掉了,此时这个) 就成了前一个不可行的位置,于是自己入队;

否则,查看当前位置到前面一个不可行位置的距离。

97. 交错字符串

给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + ... + sn
  • t = t1 + t2 + ... + tm
  • |n - m| <= 1

交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ...;注意:a + b 意味着字符串 a 和 b 连接。

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        self.ans = False 
        m = len(s1)
        n = len(s2)
        l = len(s3)
        if m + n != l:
            return False 
        dp = [[False for _ in range(n + 1)] for _ in range(m + 1)]
        def dfs(i, j, k):
            if self.ans or dp[i][j]:
                return 
            dp[i][j] = True 
            if i == m and j == n and k == l:
                self.ans = True 
                return 
            if i < m and s1[i] == s3[k]:
                dfs(i + 1, j, k + 1)
            if j < n and s2[j] == s3[k]:
                dfs(i, j + 1, k + 1)
        dfs(0, 0, 0)
        return self.ans

121. 买卖股票的最佳时机 I

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。如果你不能获取任何利润,返回 0 。

输入:[7,1,5,3,6,4];输出:5

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        curmin = prices[0]
        ans = 0
        for price in prices:
            ans = max(ans, price - curmin)
            curmin = min(curmin, price)
        return ans

121. 买卖股票的最佳时机 II

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。然而,你可以在 同一天 多次买卖该股票,但要确保你持有的股票不超过一股。返回 你能获得的 最大 利润 。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        res = 0
        n = len(prices)
        for i in range(n - 1):
            if prices[i + 1] - prices[i] > 0:
                res += (prices[i + 1] - prices[i])
        return res

188. 买卖股票的最佳时机 IV

给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

LeetCode 上 III 和 IV 实际上是同一道题。只不过一个hardcode了次数为3,一个写成了 k,思路都是一样的。

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        FREE = 0
        HOLD = 1
        n = len(prices)
        dp = [[[0, 0] for _ in range(k + 1)] for _ in range(n + 1)]
        for i in range(k + 1):
            dp[0][i][HOLD] = - prices[0]

        for i in range(1, n + 1):
            for j in range(1, k + 1):
                dp[i][j][FREE] = max(dp[i - 1][j][FREE], dp[i - 1][j][HOLD] + prices[i - 1])
                dp[i][j][HOLD] = max(dp[i - 1][j][HOLD], dp[i - 1][j - 1][FREE] - prices[i - 1])

        return dp[-1][-1][FREE]

309. 买卖股票的最佳时机含冷冻期

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

输入: prices = [1,2,3,0,2] 输出: 3 ,实际就是在原先的 dp 基础上新增一个冷冻状态;

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0
        f0 = float('-inf')
        f1 = 0
        f2 = 0 
        for price in prices:
            f0_n = max(f0, f2 - price)
            f1_n = max(f1, f0 + price)
            f2_n = max(f1, f2)
            f0, f1, f2 = f0_n, f1_n, f2_n

        return max(f1, f2)

714. 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2 输出:8

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        FREE = 0
        HOLD = 1
        n = len(prices)
        dp = [[0, 0] for _ in range(n + 1)]
        dp[0][HOLD] = - prices[0] 
        for i in range(1, n + 1):
            dp[i][FREE] = max(dp[i - 1][FREE], dp[i - 1][HOLD] + prices[i - 1] - fee)
            dp[i][HOLD] = max(dp[i - 1][HOLD], dp[i - 1][FREE] - prices[i - 1])
        return dp[-1][FREE]