动态规划的核心就是最优化问题。核心是穷举。
判断可不可以用动态规划:当前的状态取决于前一个时刻的状态。
动态规划的特点: 1、重叠子问题 2、状态转移方程 3、最优子结构
解题套路: 1、明确状态 2、明确选择 3、明确dp函数/数组的定义 4、明确base case
一、一般问题系列
1.1、题目汇总
leetcode 509. 斐波那契数
leetcode 509. 斐波那契数.
class Solution:
def fib(self, n: int) -> int:
if n < 2: return n
dp = [0 for i in range(n+1)]
dp[0], dp[1] = 0, 1
for i in range(2, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
leetcode 70. 爬楼梯
leetcode 70. 爬楼梯.
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2: return n
dp = [0 for i in range(n+1)]
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
leetcode 343. 整数拆分
343. 整数拆分.
class Solution:
def integerBreak(self, n: int) -> int:
if n == 1: return 1
if n == 2: return 1
if n == 3: return 2
dp = [0 for i in range(n+1)]
dp[1] = 1
for i in range(2, n+1):
for j in range(1, i):
dp[i] = max(dp[i], j * (i-j), j * dp[i - j])
return dp[n]
剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环问题 难 地推公式没懂)
剑指 Offer 62. 圆圈中最后剩下的数字.
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
if n == 1: return 0
dp = [0 for i in range(n+1)]
dp[1] = 0
for i in range(2, n+1):
dp[i] = (dp[i-1] + m) % i
return dp[n]
二、路径规划系列
2.1、题目汇总
leetcode 62. 不同路径
62. 不同路径.
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
if m == 0 or n == 0 or not obstacleGrid or obstacleGrid[0][0] == 1: return 0
dp = [[0 for _ in range(n)] for _ in range(m)]
for i in range(n):
if obstacleGrid[0][i] == 1: break
dp[0][i] = 1
for j in range(m):
if obstacleGrid[j][0] == 1: break
dp[j][0] = 1
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 1:
dp[i][j] = 0
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
leetcode 63. 不同路径 II
63. 不同路径 II.
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
if m == 0 or n == 0 or not obstacleGrid or obstacleGrid[0][0] == 1: return 0
dp = [[0 for _ in range(n)] for _ in range(m)]
for i in range(n):
if obstacleGrid[0][i] == 1: break
dp[0][i] = 1
for j in range(m):
if obstacleGrid[j][0] == 1: break
dp[j][0] = 1
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 1:
dp[i][j] = 0
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
leetcode 64. 最小路径和
64. 最小路径和.
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [[grid[j][i] for i in range(n)] for j in range(m)]
for i in range(1, n):
dp[0][i] += dp[0][i-1]
for j in range(1, m):
dp[j][0] += dp[j-1][0]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i-1][j]+dp[i][j], dp[i][j-1]+dp[i][j])
return dp[-1][-1]
三、背包系列
3.1、0-1背包问题
0-1背包问题:有N件(0~n-1)物体和一个最多能放W重量的背包,第i件物体的重量是weight[i],价值是value[i]。每件物体只能放一次,求解哪些物体放入背包里物体价值最大?
二维dp:
- 确定dp数组及其含义
二维数组dp[i][j],表示从下标为0~i的物体中任意取,放入容量为j的背包中,价值总和最大值为dp[i][j]; - 状态转移方程/递推公式
dp[i][j]的状态可以由两个方向推导而来: A、dp[i-1][j]:表示第 i 个物体我不考虑放不放,那么背包容量为 j ,取前i个物体的最大价值就等于背包容量为 j ,取前 i-1 个物体的最大价值; B、value[i] + dp[i-1][j-weight[i]:表示第i个物体我要考虑放不放的情况下,那么只需要计算在背包容量为 j-weight[i] 的情况下,从前 i-1 个物体中取的最大价值; 所以dp[i][j] = max(dp[i-1][j],value[i] + dp[i-1][j-weight[i]]) - 确定dp初始化方式(由递推公式决定):
背包容量为0的话,什么物体都放不下,最大价值永远都是0,即dp[0~][0] = 0; 只放物品0的话,背包容量大于等于物品0的重量时等于物品0的value,小于物品0的重量时等于0;dp = [[0 for _ in range(bagWegiht+1)] for _ in range(len(weight))]
for j in (bagWeight, weight[0]-1, -1):
dp[0][j] = dp[0][j-wieght[0]] + value[0]
- 确定遍历顺序
对于二位dp的背包问题,两层for循环是可以颠倒的for i in range(1, len(weight)):
for j in range(1, bagWegiht+1):
if j < weight[i]: dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+values[i])
for j in range(1, bagWeight+1):
for i in range(1, len(weight)):
if j < weight[i]: dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+values[i])
一维dp:
- 确定dp数组及其含义
一维数组dp[j],表示容量为j的背包中,价值总和最大值为dp[j]; - 状态转移方程/递推公式
dp[i][j]的状态可以由两个方向推导而来: A、dp[j]:先考虑我不放第 i 个物体,因为二维数组中此时是:dp[i-1][j] 那么我们使用滚动数组,将i-1的情况直接覆盖在当前层,此时dp[j]就等价于二维数组中的dp[i-1][j]; B、value[i] + dp[j-weight[i]:表示我要放第i个物体,那么只需要计算在背包容量为 j-weight[i] 的情况下,从前 i-1 个物体中取的最大价值,二维数组是value[i] + dp[i-1][j-weight[i],使用滚动数组,把第i-1行的数子覆盖到当前层,所以值为value[i]+dp[j-weight[i]]; 所以dp[j] = max(dp[j],value[i] + dp[j-weight[i]]) - 确定dp初始化方式(由递推公式决定):
dp[0]=0 其他位置全部初始化为0dp = [0 for _ in range(bagWegiht+1)]
- 确定遍历顺序
对于二位dp的背包问题,两层for循环是可以颠倒的 for i in range(len(weight)):
for j in range(bagWeight, weight[i]-1, -1):
dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
模板:
在这里插入代码片
解题思路
-
传统背包问题:dp[i][j]表示在背包容量为j的情况下,从前i个物体中任意选取若干个物体,能够达到的最大重量?最多能装多少? 公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); -
转型问题1:dp[i][j]表示在背包容量为j的情况下,从前i个物体中任意选取若干个物体,能否找到重量为j的组合?能否装满背包? 公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); 转型问题1是可以等价用传统背包问题等价的,只要在遍历每个物体结束的时候,比较下当前最大重量是否等于target即可: for i in range(1, len(nums)):
for j in range(1, bagweight+1):
if j < nums[i]: dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i])
if dp[i][bagweight] == bagweight: return True
return False
-
转型问题2:dp[i][j]表示在背包容量为j的情况下,从前i个物体中任意选取若干个物体,找到重量为j的组合数目?装满背包有几种方法? 公式:dp[j] += dp[j - nums[i]]
题目汇总
416. 分割等和子集
416. 分割等和子集.
视频讲解.
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 == 1: return False
bagweight = sum(nums) // 2
dp = [[0 for _ in range(bagweight+1)] for _ in range(len(nums))]
for j in range(bagweight, nums[0]-1, -1):
dp[0][j] = nums[0]
for i in range(1, len(nums)):
for j in range(1, bagweight+1):
if j < nums[i]: dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i])
if dp[i][bagweight] == bagweight: return True
return False
if sum(nums) % 2 == 1: return False
bagweight = sum(nums) // 2
dp = [0 for j in range(bagweight+1)]
for i in range(len(nums)):
for j in range(bagweight, nums[i]-1, -1):
if j >= nums[i]:
dp[j] = max(dp[j], dp[j-nums[i]]+nums[i])
if dp[bagweight] == bagweight: return True
return False
494. 目标和
494. 目标和.
文字讲解.
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
sum_nums = sum(nums)
if (sum_nums - target) % 2 == 1: return 0
if target > sum_nums: return 0
bagweight = (sum_nums - target) // 2
dp = [0 for _ in range(bagweight+1)]
dp[0] = 1
for i in range(len(nums)):
for j in range(bagweight, nums[i]-1, -1):
dp[j] += dp[j-nums[i]]
return dp[-1]
3.2、完全背包问题
模板:
def test_complete_pack1(nums):
bagweight = ?
dp = [0 for _ in range(bagweight + 1)]
for i in range(len(nums)):
for j in range(nums[i], bagweight + 1):
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return dp[-1]
题目汇总
518. 零钱兑换 II
518. 零钱兑换 II.
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0 for _ in range(amount+1)]
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i], amount+1):
if j >= coins[i]:
dp[j] += dp[j-coins[i]]
return dp[-1]
322. 零钱兑换
322. 零钱兑换.
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [float('inf') for _ in range(amount+1)]
dp[0] = 0
for i in range(len(coins)):
for j in range(coins[i], amount+1):
if j >= coins[i]:
dp[j] = min(dp[j], dp[j-coins[i]]+1)
return dp[-1] if dp[-1] != float('inf') else -1
四、打家劫舍系列
【专题讲解】打家劫舍 leetcode 198 213 337.
198. 打家劫舍
198. 打家劫舍.
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) == 0: return 0
if len(nums) == 1: return nums[0]
dp = [0 for _ in range(len(nums))]
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i-1], dp[i-2]+nums[i])
return dp[-1]
213. 打家劫舍 II
213. 打家劫舍 II.
class Solution:
def helper(self, nums):
if len(nums) <= 0: return 0
if len(nums) == 1: return nums[0]
dp = [0 for _ in range(len(nums))]
dp[0] = dp[0]
dp[1] = max(dp[0], dp[1])
for i in range(len(nums)):
dp[i] = max(dp[i-1], dp[i-2]+nums[i])
return dp[-1]
def rob(self, nums: List[int]) -> int:
if len(nums) <= 0: return 0
if len(nums) == 1: return nums[0]
nums1 = self.helper(nums[1:-1])
nums2 = self.helper(nums[:-1])
nums3 = self.helper(nums[1:])
return max(nums1, nums2, nums3)
五、股票问题系列
LeetCode121-123 动态规划解决股票问题 LeetCode188+LeetCode309+LeetCode714 动态规划解决股票问题
121. 买卖股票的最佳时机
题意:求买卖一只股票获得的最大利润(只能完成一次交易)
121. 买卖股票的最佳时机.
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) < 2: return 0
dp = [[0 for _ in range(2)] for _ in range(len(prices))]
dp[0][0], dp[0][1] = 0, -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], - prices[i])
return dp[len(prices)-1][0]
if len(prices) < 2: return 0
dp = [0 for _ in range(2)]
dp[0], dp[1] = 0, -prices[0]
for i in range(1, len(prices)):
dp[0] = max(dp[0], dp[1] + prices[i])
dp[1] = max(dp[1], -prices[i])
return dp[0]
122. 买卖股票的最佳时机 II
题意:可以买卖多次(无限次),但是一个时刻只能持有一支股票,求最大利润?
122. 买卖股票的最佳时机 II.
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) == 0: return 0
dp = [[0 for _ in range(2)] for _ in range(len(prices))]
dp[0][0], dp[0][1] = 0, -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
return dp[len(prices)-1][0]
if len(prices) == 0: return 0
dp = [0, -prices[0]]
for i in range(1, len(prices)):
dp[0] = max(dp[0], dp[1] + prices[i])
dp[1] = max(dp[1], dp[0] - prices[i])
return dp[0]
309. 最佳买卖股票时机含冷冻期(包含I、II 代码通过了但是有疑问???)
题意:可以买卖无数次,且一个时刻只能持有一支股票(不能同时参与多笔交易),卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天),求最大利润?
309. 最佳买卖股票时机含冷冻期.
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) < 2: return 0
dp = [[0 for _ in range(2)] for _ in range(len(prices))]
dp[0][0] = 0
dp[0][1] = -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i])
return dp[len(prices)-1][0]
123. 买卖股票的最佳时机 III
题意:最多可以买卖2次,且一个时刻只能持有一支股票(不能同时参与多笔交易),求最大利润?
123. 买卖股票的最佳时机 III.
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) == 0: return 0
dp = [[[0 for _ in range(3)] for _ in range(2)]for _ in range(len(prices))]
dp[0][0][0] = 0
dp[0][1][0] = -prices[0]
dp[0][0][1] = dp[0][1][1] = dp[0][0][2] = dp[0][1][2] = float('-inf')
for i in range(1, len(prices)):
dp[i][0][0] = 0
dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0]-prices[i])
dp[i][0][1] = max(dp[i-1][0][1], dp[i-1][1][0]+prices[i])
dp[i][1][1] = max(dp[i-1][1][1], dp[i-1][0][1]-prices[i])
dp[i][0][2] = max(dp[i-1][0][2], dp[i-1][1][1]+prices[i])
dp[i][1][2] = float('-inf')
return max(0, dp[len(prices)-1][0][1], dp[len(prices)-1][0][2])
if len(prices) == 0: return 0
dp = [[0 for _ in range(3)] for _ in range(2)]
dp[0][0] = 0
dp[1][0] = -prices[0]
dp[0][1] = dp[1][1] = dp[0][2] = dp[1][2] = float('-inf')
for i in range(1, len(prices)):
dp[0][0] = 0
dp[1][0] = max(dp[1][0], dp[0][0]-prices[i])
dp[0][1] = max(dp[0][1], dp[1][0]+prices[i])
dp[1][1] = max(dp[1][1], dp[0][1]-prices[i])
dp[0][2] = max(dp[0][2], dp[1][1]+prices[i])
dp[1][2] = float('-inf')
return max(0, dp[0][1], dp[0][2])
188. 买卖股票的最佳时机 IV(包含III)
题意:最多可以买卖k次,且一个时刻只能持有一支股票(不能同时参与多笔交易),求最大利润?
188. 买卖股票的最佳时机 IV.
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
if len(prices) == 0: return 0
dp = [[[0 for _ in range(k+1)] for _ in range(2)] for _ in range(len(prices))]
dp[0][0][0] = 0
dp[0][1][0] = -prices[0]
for i in range(1, k+1):
dp[0][0][i] = float('-inf')
dp[0][1][i] = float('-inf')
for i in range(1, len(prices)):
for j in range(0, k+1):
if j == 0:
dp[i][0][0] = 0
dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][0][0]-prices[i])
else:
dp[i][0][j] = max(dp[i-1][0][j], dp[i-1][1][j-1]+prices[i])
dp[i][1][j] = max(dp[i-1][1][j], dp[i-1][0][j]-prices[i])
res = float("-inf")
for i in range(k+1):
res = max(res, dp[len(prices)-1][0][i])
return res
if len(prices) == 0: return 0
dp = [[0 for _ in range(k+1)] for _ in range(2)]
dp[0][0] = 0
dp[1][0] = -prices[0]
for i in range(1, k+1):
dp[0][i] = float('-inf')
dp[1][i] = float('-inf')
for i in range(1, len(prices)):
for j in range(0, k+1):
if j == 0:
dp[0][0] = 0
dp[1][0] = max(dp[1][0], dp[0][0]-prices[i])
else:
dp[0][j] = max(dp[0][j], dp[1][j-1]+prices[i])
dp[1][j] = max(dp[1][j], dp[0][j]-prices[i])
res = float("-inf")
for i in range(k+1):
res = max(res, dp[0][i])
return res
六、子序列子数组问题系列
题目汇总
- 子数组必须是连续的,子序列不一定要连续的;
674. 最长连续递增子序列
题意:寻找数组中最长连续递增子序列的长度并找到这个子序列打印出来?
674. 最长连续递增序列.
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
if len(nums) < 2: return len(nums)
dp = [1 for _ in range(len(nums))]
max_len = 1
s_end = 1
for i in range(1, len(nums)):
if nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1
if dp[i] > max_len:
max_len = dp[i]
s_end = i
return max_len
300. 最长递增子序列(经典)
题意:寻找数组中最长递增子序列的长度?
300. 最长递增子序列.
视频讲解.
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if len(nums) < 2: return len(nums)
dp = [1 for _ in range(len(nums))]
max_len = 1
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
if max_len < dp[i]: max_len = dp[i]
return max_len
53. 最大子数组和
题意:寻找数组中连续子序列的最大和?
53. 最大子数组和.
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if len(nums) == 0: return 0
if len(nums) == 1: return nums[0]
dp = nums
max_sum = dp[0]
for i in range(1, len(nums)):
dp[i] = max(dp[i], dp[i-1]+nums[i])
if max_sum < dp[i]:
max_sum = dp[i]
return max_sum
152. 乘积最大子数组
题意:寻找数组中连续子序列的最大乘积?
152. 乘积最大子数组.
class Solution:
def maxProduct(self, nums: List[int]) -> int:
if len(nums) == 0: return 0
if len(nums) == 1: return nums[0]
max_mul = nums[0]
dp_max = [nums[i] for i in range(len(nums))]
dp_min = [nums[i] for i in range(len(nums))]
for i in range(1, len(nums)):
dp_max[i] = max(dp_max[i], dp_max[i-1]*nums[i], dp_min[i-1]*nums[i])
dp_min[i] = min(dp_min[i], dp_max[i-1]*nums[i], dp_min[i-1]*nums[i])
max_mul = max(dp_max[i], dp_min[i], max_mul)
return max_mul
1143. 最长公共子序列(经典)
题意:求两个字符串的最长公共子序列(可以不连续)?扩展:怎么找到这个子序列呢(打印出来)?
1143. 最长公共子序列.
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
if not text1 or not text2: return 0
dp = [[0 for _ in range(len(text2)+1)] for _ in range(len(text1)+1)]
max_len = 0
for i in range(1, len(text1)+1):
for j in range(1, len(text2)+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
if dp[i][j] > max_len: max_len = dp[i][j]
return max_len
718. 最长公共子数组
题意:求两个字符串的最长公共子数组(必须连续),找出来并打印
718. 最长重复子数组.
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
if not nums1 or not nums2: return 0
dp = [[0 for _ in range(len(nums2)+1)] for _ in range(len(nums1)+1)]
max_len = 0
s_end = 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
if dp[i][j] > max_len:
max_len = dp[i][j]
s_end = i
return max_len
718. 最长公共子数组
题意:求两个字符串的最长公共子数组(必须连续),找出来并打印
718. 最长重复子数组.
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
if not nums1 or not nums2: return 0
dp = [[0 for _ in range(len(nums2)+1)] for _ in range(len(nums1)+1)]
max_len = 0
s_end = 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
if dp[i][j] > max_len:
max_len = dp[i][j]
s_end = i
return max_len
647. 回文子串
题意:字符串中统计回文子串的个数 647. 回文子串.
class Solution:
def countSubstrings(self, s: str) -> int:
if len(s) == 1: return 1
res = 0
dp = [[False for _ in range(len(s))] for _ in range(len(s))]
for i in range(len(s)):
dp[i][i] = True
res += 1
for i in range(len(s)-1, -1, -1):
for j in range(i+1, len(s)):
if s[i] == s[j]:
if j - i < 2: dp[i][j] = True
else: dp[i][j] = dp[i+1][j-1]
if dp[i][j]: res += 1
return res
5. 最长回文子串(经典)
题意:找到其中一个最长回文子串。提高:找到所有最长回文子串?
5. 最长回文子串.
class Solution:
def longestPalindrome(self, s: str) -> str:
if len(s) < 2: return s
max_len = 1
s_start, s_end = 0, 0
dp = [[False for _ in range(len(s))] for _ in range(len(s))]
for i in range(len(s)):
dp[i][i] = True
for i in range(len(s)-2, -1, -1):
for j in range(i+1, len(s)):
if s[i] == s[j]:
if j - i < 2: dp[i][j] = True
else: dp[i][j] = dp[i+1][j-1]
if dp[i][j] and (j-i+1) > max_len:
max_len = j - i + 1
s_start = i
s_end = j
return s[s_start:s_end+1]
516. 最长回文子序列(经典)
516. 最长回文子序列.
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
if len(s) < 2: return len(s)
dp = [[0 for _ in range(len(s))] for _ in range(len(s))]
max_len = 1
for i in range(len(s)):
dp[i][i] = 1
for i in range(len(s)-2, -1, -1):
for j in range(i+1, len(s)):
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
if dp[i][j] > max_len:
max_len = dp[i][j]
return max_len
七、字符串问题系列
- 在字符串的前面加上空字符之后,会让初始化操作变得简单了很多, 类似于哨兵的那种原理。
- 如果涉及到两个字符串的动规问题,一般都是二维dp
题目汇总
72. 编辑距离
72. 编辑距离.
视频讲解.
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
if len(word2) == 0: return len(word1)
if len(word1) == 0: return len(word2)
dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
for i in range(1, len(word1)+1):
dp[i][0] = i
for j in range(1, len(word2)+1):
dp[0][j] = j
for i in range(1, len(word1)+1):
for j in range(1, len(word2)+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
return dp[-1][-1]
牛客Top200高频:最小编辑代码
题意:和上一题几乎一样,就是换了评价方式
牛客Top200高频:最小编辑代码.
class Solution:
def minEditCost(self , str1: str, str2: str, ic: int, dc: int, rc: int) -> int:
if len(str1) == 0: return len(str2) * ic
if len(str2) == 0: return len(str1) * ic
dp = [[0 for _ in range(len(str2)+1)] for _ in range(len(str1)+1)]
for i in range(len(str1)+1):
dp[i][0] = i * ic
for j in range(len(str2)+1):
dp[0][j] = j * ic
for i in range(1, len(str1)+1):
for j in range(1, len(str2)+1):
if str1[i-1] == str2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j]+dc, dp[i][j-1]+ic, dp[i-1][j-1]+rc)
return dp[-1][-1]
LeetCode583: 两个字符串的删除操作
LeetCode583: 两个字符串的删除操作.
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
if len(word1) == 0: return len(word2)
if len(word2) == 0: return len(word1)
dp = [[0 for _ in range(len(word2)+1)] for _ in range(len(word1)+1)]
for i in range(1, len(word1)+1):
dp[i][0] = i
for j in range(1, len(word2)+1):
dp[0][j] = j
for i in range(1, len(word1)+1):
for j in range(1, len(word2)+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]) + 1
return dp[-1][-1]
LeetCode32. 最长有效括号(难)
32. 最长有效括号.
视频讲解.
class Solution:
def longestValidParentheses(self, s: str) -> int:
if len(s) == 0: return 0
max_len = 0
dp = [0 for _ in range(len(s))]
for i in range(1, len(s)):
if s[i] == '(': dp[i] = 0
else:
if s[i-1] == '(' : dp[i] = dp[i-2] + 2
elif s[i-dp[i-1]-1] == '(' and (i-dp[i-1]-1)>=0:
dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2]
if dp[i] > max_len: max_len = dp[i]
return max_len
圆环回原点问题(待更新)
字节的高频面试题目
问题描述:
圆环上有10个点,编号为0~9。从0点出发,每次可以逆时针和顺时针走一步,问走n步回到0点共有多少种走法。
输入: 2
输出: 2
解释: 两种方案。分别是0->1->0和0->9->0
Reference
算法刷题重温(八): 硬核动态规划.
|