# from functools import lru_cache classSolution: # @lru_cache(10**8) defclimbStairs(self, n: int) -> int: if n <= 2: return n return self.climbStairs(n - 1) + self.climbStairs(n - 2)
第二种方法:动态规划,用数组记录每个台阶的所有走法个数,时间复杂度降为 $O(n)$
1 2 3 4 5 6 7 8 9
classSolution: defclimbStairs(self, n: int) -> int: if n <= 2: return n f = [0] * (n+1) f[0] = f[1] = 1 for i in range(2, n+1): f[i] = f[i-1] + f[i-2] return f[n]
classSolution: defminimumTotal(self, triangle: List[List[int]]) -> int: lens = len(triangle) mini = [[0]*lens for _ in range(lens)] mini[lens-1] = triangle[lens-1] for i in range(lens-2, -1, -1): for j in range(len(triangle[i])): mini[i][j] = triangle[i][j] + min(mini[i+1][j], mini[i+1][j+1]) return mini[0][0]
第三种方法:在第二种方法的基础上,mini只需一维数组即可,更新自身
1 2 3 4 5 6 7
classSolution: defminimumTotal(self, triangle: List[List[int]]) -> int: mini = triangle[len(triangle)-1] for i in range(len(triangle)-2, -1, -1): for j in range(len(triangle[i])): mini[j] = triangle[i][j] + min(mini[j], mini[j+1]) return mini[0]
第四种方法:在第二种方法的基础上,直接修改三角形数组的值,空间复杂度为O(1)
1 2 3 4 5 6
classSolution: defminimumTotal(self, triangle: List[List[int]]) -> int: for i in range(len(triangle)-2, -1, -1): for j in range(len(triangle[i])): triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]) return triangle[0][0]
classSolution: defmaxProduct(self, nums: List[int]) -> int: if nums isNone: return0 dp = [[0]*2for _ in range(len(nums))] dp[0][0], dp[0][1] = nums[0], nums[0] for i in range(1, len(nums)): dp[i][0] = max(dp[i-1][0]*nums[i], dp[i-1][1]*nums[i], nums[i]) dp[i][1] = min(dp[i-1][0]*nums[i], dp[i-1][1]*nums[i], nums[i]) result = [] for j in dp: result.append(j[0]) return max(result)
第二种方法:在第一种方法的基础上,用一维滚动数组dp,每次循环交替x和y为0和1
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: defmaxProduct(self, nums: List[int]) -> int: if nums isNone: return0 dp = [[0]*2for _ in range(2)] dp[0][0], dp[0][1], res = nums[0], nums[0], nums[0] for i in range(1, len(nums)): x, y = i & 1, (i - 1) & 1 dp[x][0] = max(dp[y][0]*nums[i], dp[y][1]*nums[i], nums[i]) dp[x][1] = min(dp[y][0]*nums[i], dp[y][1]*nums[i], nums[i]) res = max(res, dp[x][0]) return res
classSolution: deflengthOfLIS(self, nums: List[int]) -> int: ifnot nums: return0 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)
classSolution: defcoinChange(self, coins: List[int], amount: int) -> int: if amount < 1: return0 self.coins = sorted(coins, reverse=True) self.res = [] for i in self.coins: self.dfs(amount, i, 1) ifnot self.res: return-1 return min(self.res) defdfs(self, amount, num, count): last = amount - num if last < 0: return ifnot last: self.res.append(count) return for i in self.coins: self.dfs(last, i, count + 1)
第二种方法:DP动态规划,定义状态dp[i]为拼凑数额i最少所需的硬币数量
1 2 3 4 5 6 7 8 9
classSolution: defcoinChange(self, coins: List[int], amount: int) -> int: dp = [amount+1]*(amount+1) dp[0] = 0 for i in range(1, amount+1): for c in coins: if i - c >= 0: dp[i] = min(dp[i], dp[i-c] + 1) return dp[amount] if dp[amount] <= amount else-1
第三种方法:把coins的循环放外层,减少循环次数及一次if判断
1 2 3 4 5 6 7
classSolution: defcoinChange(self, coins: List[int], amount: int) -> int: dp = [0] + [amount+1]*amount for coin in coins: for i in range(coin, amount+1): dp[i] = min(dp[i], dp[i-coin]+1) return dp[-1] if dp[-1] != amount+1else-1
classSolution: defminDistance(self, word1: str, word2: str) -> int: n, m = len(word1), len(word2) dp = [[0]*(m+1) for _ in range(n+1)] for i in range(n+1): dp[i][0] = i for j in range(m+1): dp[0][j] = j for i in range(1,n+1): for j in range(1,m+1): if word1[i-1] == word2[j-1]: dp[i][j] = dp[i-1][j-1] else: dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1 # 上面的if else逻辑可以压缩成一行 # dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+(0 if word1[i-1] == word2[j-1] else 1)) return dp[n][m]
classSolution: deftrap(self, height: List[int]) -> int: ifnot height: return0 n, res = len(height), 0 maxLeft, maxRight = [0]*n, [0]*n maxLeft[0] = height[0] maxRight[-1] = height[-1] for i in range(1, n): maxLeft[i] = max(height[i], maxLeft[i-1]) for j in range(n-2, -1, -1): maxRight[j] = max(height[j], maxRight[j+1]) for k in range(n): res += min(maxLeft[k], maxRight[k]) - height[k] return res
classSolution: defuniquePaths(self, m: int, n: int) -> int: ifnot m ornot n: return0 dp = [[1]*m for _ in range(n)] for i in range(n-2, -1, -1): for j in range(m-2, -1, -1): dp[i][j] = dp[i+1][j] + dp[i][j+1] return dp[0][0]
classSolution: defuniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: ifnot obstacleGrid ornot obstacleGrid[0]: return0 n, m = len(obstacleGrid), len(obstacleGrid[0]) dp = [[1]*m for _ in range(n)] # 处理最右列的初始值 flag = 0 for i in range(n-1, -1, -1): if flag: dp[i][m-1] = 0 continue if obstacleGrid[i][m-1]: dp[i][m-1] = 0 flag = 1 # 处理最下行的初始值 flag = 0 for i in range(m-1, -1, -1): if flag: dp[n-1][i] = 0 continue if obstacleGrid[n-1][i]: dp[n-1][i] = 0 flag = 1 # 从右下到左上的DP递推 for i in range(n-2, -1, -1): for j in range(m-2, -1, -1): if obstacleGrid[i][j]: dp[i][j] = 0 continue dp[i][j] = dp[i+1][j] + dp[i][j+1] return dp[0][0]
classSolution: defminPathSum(self, grid: List[List[int]]) -> int: ifnot grid ornot grid[0]: return0 n, m = len(grid), len(grid[0]) dp = [[0]*m for _ in range(n)] dp[-1][-1] = grid[-1][-1] for i in range(n-2, -1, -1): dp[i][m-1] = grid[i][m-1] + dp[i+1][m-1] for i in range(m-2, -1, -1): dp[n-1][i] = grid[n-1][i] + dp[n-1][i+1] for i in range(n-2, -1, -1): for j in range(m-2, -1, -1): dp[i][j] = min(dp[i+1][j], dp[i][j+1]) + grid[i][j] return dp[0][0]
用滚动数组降低空间复杂度,从左上角往右下角迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution: defminPathSum(self, grid: List[List[int]]) -> int: ifnot grid ornot grid[0]: return0 m, n = len(grid), len(grid[0]) dp = [[0]*n for _ in range(2)] dp[0][0] = grid[0][0]
for i in range(1, n): dp[0][i] = dp[0][i-1] + grid[0][i] for i in range(1, m): x, y = i&1, (i-1)&1 for j in range(n): ifnot j: dp[x][j] = dp[y][j] + grid[i][j] else: dp[x][j] = min(dp[y][j], dp[x][j-1]) + grid[i][j] return dp[0][-1] if m&1else dp[1][-1]
第一种方法:动态规划,定义dp[i][j]为从位置j到i的字符串是否是回文串,递推式子是if (s[i] == s[j] and dp[i-1][j+1]) then dp[i][j] = 1其中加入一个判断如果子串长度为2就不用看dp了加速计算。然后如果dp[i][j]是回文串了,就跟当前最长的回文串比较,如果新的更长就更新结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: deflongestPalindrome(self, s: str) -> str: ifnot s: return'' n = len(s) maxLen, res, dp = 0, '', [[0]*n for _ in range(n)] for i in range(n): for j in range(i, -1, -1): if s[i] == s[j] and (i-j<2or dp[i-1][j+1]): dp[i][j] = 1 if dp[i][j] and maxLen < i-j+1: maxLen = i-j+1 res = s[j:i+1] return res