加到【编辑距离】中
剑指 Offer 46. 把数字翻译成字符串
class Solution:
def translateNum(self, num: int) -> int:
numbers = str(num)
L = len(numbers)
dp=[1]*L
for i in range(1, L):
dp[i] = dp[i-1] + (dp[i-2] if '10'<=numbers[i-1:i+1]<='25' else 0)
return dp[L-1]
class Solution:
def translateNum(self, num: int) -> int:
s=str(num)
n=len(s)
dp=[1]+[0]*n
for i in range(1, n+1):
# if s[i-1]!='0': # diff
dp[i]+=dp[i-1] # diff
if i>1 and '10'<=s[i-2:i]<'26': # <= diff
dp[i]+=dp[i-2]
return dp[-1]
91. 解码方法
class Solution:
def numDecodings(self, s: str) -> int:
n=len(s)
dp=[1]+[0]*n
for i in range(1, n+1):
if s[i-1]!='0':
dp[i]+=dp[i-1]
if i>1 and '10'<=s[i-2:i]<='26':
dp[i]+=dp[i-2]
return dp[-1]
174. 地下城游戏
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
m = len(dungeon)
n = len(dungeon[0])
dp = [[inf] * (n + 1) for _ in range(m + 1)]
dp[m][n - 1] = dp[m - 1][n] = 1
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
# minn: 走出这个格子至少能剩下多少体力
minn = min(dp[i + 1][j], dp[i][j + 1])
# dp_{i,j}: 走出这个格子至少要具备多少体力
dp[i][j] = max(minn - dungeon[i][j], 1)
return dp[0][0]
403. 青蛙过河
时间复杂度: O ( N 2 ) O(N^2) O(N2)
class Solution:
def canCross(self, stones: List[int]) -> bool:
mapper={}
for stone in stones:
mapper[stone]=set()
mapper[0].add(0)
for stone in stones:
for k in mapper[stone]:
for step in range(k-1,k+2):
if step>0 and stone+step in mapper:
mapper[stone+step].add(step)
return len(mapper[stones[-1]])>0
62. 不同路径
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp=[[0]*(n+1) for _ in range(m+1)]
dp[1][1]=1
for i in range(1, m+1):
for j in range(1, n+1):
if i==1 and j==1:
continue
dp[i][j]=dp[i-1][j]+dp[i][j-1]
return dp[m][n]
63. 不同路径 II
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m=len(obstacleGrid)
n=len(obstacleGrid[0])
dp=[[0]*(n+1) for _ in range(m+1)]
# dp[1][1]=1
for i in range(1, m+1):
for j in range(1, n+1):
if obstacleGrid[i-1][j-1]==1:
continue
if i==1 and j==1:
dp[1][1]=1
continue
dp[i][j]=dp[i-1][j]+dp[i][j-1]
return dp[m][n]
188. 买卖股票的最佳时机 IV
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
N=len(prices)
K=k
dp=[[[0]*2 for _ in range(K+1)] for _ in range(N+1)]
for k in range(K+1):
dp[0][k][1]=-inf
for i in range(N+1):
dp[i][0][1]=-inf
for i in range(1, N+1):
# for k in range(K,0,-1):
for k in range(1, K+1):
# 卖
dp[i][k][0]=max(dp[i-1][k][0], dp[i-1][k][1]+prices[i-1])
# 买
dp[i][k][1]=max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i-1])
return dp[N][K][0]
309. 最佳买卖股票时机含冷冻期
class Solution:
def maxProfit(self, prices: List[int]) -> int:
N=len(prices)
dp=[[0]*2 for _ in range(N+1)]
dp[0][1]=-inf
for i in range(1, N+1):
# 卖
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1])
# 买
dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i-1])
return dp[N][0]
714. 买卖股票的最佳时机含手续费
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
if not prices:
return 0
N = len(prices)
dp_i_0 = 0
dp_i_1 = -inf
for i in range(N):
dp_i_0 = max(dp_i_0, dp_i_1 + prices[i] - fee)
dp_i_1 = max(dp_i_1, dp_i_0 - prices[i])
return dp_i_0
718. 最长重复子数组
O ( N M ) O(NM) O(NM)
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
n, m = len(A), len(B)
dp = [[0] * (m + 1) for _ in range(n + 1)]
ans = 0
for i in range(1,n+1):
for j in range(1,m+1):
dp[i][j] = dp[i - 1][j - 1] + 1 if A[i-1] == B[j-1] else 0
ans = max(ans, dp[i][j])
return ans
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
def maxLength(addA: int, addB: int, length: int) -> int:
ret = k = 0
for i in range(length):
if A[addA + i] == B[addB + i]:
k += 1
ret = max(ret, k)
else:
k = 0
return ret
n, m = len(A), len(B)
ret = 0
for i in range(n):
length = min(m, n - i)
ret = max(ret, maxLength(i, 0, length))
for i in range(m):
length = min(n, m - i)
ret = max(ret, maxLength(0, i, length))
return ret
class Solution {
public:
int findLength(vector<int> &A, vector<int> &B) {
int m = A.size(), n = B.size(), res = 0;
// 枚举对应关系
for (int diff = -(m - 1); diff <= n - 1; ++diff) {
// 遍历公共部分
for (int i = max(0, -diff), l = 0; i < min(m, n - diff); ++i) {
l = (A[i] == B[i + diff]) ? (l + 1) : 0;
res = max(res, l);
}
}
return res;
}
};
516. 最长回文子序列
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
# 初始化1
for i in range(n):
dp[i][i] = 1
# 初始化2
for i in range(n - 1):
dp[i][i + 1] = 2 if s[i] == s[i + 1] else 1
# 遍历
# 间隔
for k in range(2,n):
# 起点
for i in range(n - k):
# 终点
j = i + k
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])
return dp[0][n - 1]
97. 交错字符串
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
l1=len(s1)
l2=len(s2)
l3=len(s3)
if l1+l2!=l3:
return False
dp=[[False]*(l2+1) for _ in range(l1+1)]
dp[0][0]=True
for i in range(1,l1+1):
dp[i][0]=(dp[i-1][0] and s1[i-1]==s3[i-1])
for i in range(1,l2+1):
dp[0][i]=(dp[0][i-1] and s2[i-1]==s3[i-1])
for i in range(1,l1+1):
for j in range(1,l2+1):
if s3[i+j-1]==s1[i-1] and dp[i-1][j]:
dp[i][j]=True
elif s3[i+j-1]==s2[j-1] and dp[i][j-1]:
dp[i][j]=True
return dp[l1][l2]
416. 分割等和子集
todo: 条件可以改成尽量分成两个子集,求选中了哪些物品
其实这题就是转化过的01背包
class Solution:
def canPartition(self, nums: List[int]) -> bool:
L = len(nums)
sum_ = sum(nums)
if sum_ % 2:
return False
target = sum_ // 2
# L 行 target + 1 列
dp = [[0] * (target + 1) for _ in range(L + 1)]
# 容量为0的时候,绝逼恰好能装满
dp[0][0] = True
# 处理数据,让nums从1索引
nums.insert(0,0)
# 开始DP
for i in range(1, L+1):
for j in range(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]
# 只装i个物品就装满了
if dp[i][target]:
return True
return False
312. 戳气球
经典动态规划:戳气球问题
有空再研究
class Solution:
def maxCoins(self, nums: List[int]) -> int:
n = len(nums)
points = [1] * (n + 2)
for i in range(n):
points[i + 1] = nums[i]
dp = [[0] * (n + 2) for _ in range(n + 2)]
for itv in range(2, n+2): # [2,n+1]
for i in range(0, n - itv + 2):
j = i + itv
for k in range(i + 1, j):
dp[i][j] = max(
dp[i][j],
dp[i][k] + dp[k][j] + points[i] * points[j] * points[k],
)
return dp[0][n + 1]
72. 编辑距离
Solution:
def minDistance(self, word1: str, word2: str) -> int:
l1 = len(word1)
l2 = len(word2)
dp = [[0] * (l2 + 1) for _ in range(l1 + 1)]
# 错在没+1
for i in range(1, l1 + 1):
dp[i][0] = i
for i in range(1, l2 + 1):
dp[0][i] = i
for i in range(1, l1 + 1):
for j in range(1, l2 + 1):
# 错在没+1
left = dp[i][j - 1] + 1
down = dp[i - 1][j] + 1
ld = dp[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
ld += 1
dp[i][j] = min(left, down, ld)
return dp[l1][l2]
插入删除替换的惩罚不一样的编辑距离,面经也有提到
编辑距离(levenshtein)算法
322. 零钱兑换
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 语义:凑出dp_i 的金额需要的硬币数
dp=[inf]*(amount+1)
dp[0]=0
for i in range(1, amount+1):
cur=inf
for coin in coins:
if i-coin>=0:
cur=min(cur, dp[i-coin])
dp[i]=cur+1
return -1 if dp[amount]==inf else dp[amount]
518. 零钱兑换 II
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp=[0]*(amount+1)
dp[0]=1
for coin in coins:
for x in range(coin, amount+1):
dp[x]+=dp[x-coin]
return dp[amount]
10. 正则表达式匹配
class Solution:
def isMatch(self, s: str, p: str) -> bool:
ls=len(s)
lp=len(p)
def match(i,j):
if i<=0 or j<=0:
return False
if p[j-1]=='.':
return True
if s[i-1]==p[j-1]:
return True
return False
dp=[[False]*(lp+1) for _ in range(ls+1)]
dp[0][0]=True
for i in range(ls+1):
for j in range(1, lp+1):
if p[j-1]=='*':
if match(i,j-1):
dp[i][j] = dp[i][j-2] | dp[i-1][j]
else:
dp[i][j] = dp[i][j-2]
else:
if match(i,j):
dp[i][j] |= dp[i-1][j-1]
else:
dp[i][j]=False
return dp[ls][lp]
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for i in range(1, n + 1):
if p[i - 1] == '*':
dp[0][i] = True
else:
break
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == '*':
dp[i][j] = dp[i][j - 1] | dp[i - 1][j]
elif p[j - 1] == '?' or s[i - 1] == p[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
return dp[m][n]
198. 打家劫舍
乱写的
class Solution:
def rob(self, nums: List[int]) -> int:
if not nums:
return 0
n=len(nums)
dp=[0]*(n+1) # 负索引
dp[0]=nums[0]
for i in range(1,n):
dp[i]=max(dp[i-1],dp[i-2]+nums[i]) # 负索引
return max(dp)