interview_dp
63. 不同路径 II
class Solution: def uniquePathsWithObstacles1(self, obstacleGrid: List[List[int]]) -> int: m, n = len(obstacleGrid), len(obstacleGrid[0]) if obstacleGrid[0][0] == 1 or obstacleGrid[-1][-1] == 1: return 0 dp = [[0]*n for _ in range(m)] dp[0][0] = 1 # dp[i][j] 从(0,0)出发到(i,j)的不同路径数 for j in range(1, n): dp[0][j] = 1 if (dp[0][j-1] != 0 and obstacleGrid[0][j] != 1) else 0 for i in range(1, m): dp[i][0] = 1 if (dp[i-1][0] != 0 and obstacleGrid[i][0] != 1) else 0 for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i-1][j] + dp[i][j-1] if obstacleGrid[i][j] != 1 else 0 return dp[-1][-1] def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: m, n = len(obstacleGrid), len(obstacleGrid[0]) if obstacleGrid[0][0] == 1 or obstacleGrid[-1][-1] == 1: return 0 dp = [0]*n dp[0] = 1 for j in range(1, n): dp[j] = 1 if dp[j-1] != 0 and obstacleGrid[0][j] != 1 else 0 for i in range(1, m): dp[0] = 1 if dp[0] != 0 and obstacleGrid[i][0] != 1 else 0 for j in range(1, n): dp[j] = dp[j-1] + dp[j] if obstacleGrid[i][j] != 1 else 0 return dp[-1]
给定三个字符串 s1
、s2
、s3
,请你帮忙验证 s3
是否是由 s1
和 s2
交错 组成的。
![](https://img2022.cnblogs.com/blog/891134/202211/891134-20221120000714787-1777644222.png)
class Solution: def isInterleave(self, s1: str, s2: str, s3: str) -> bool: n1, n2, n3 = len(s1), len(s2), len(s3) # dp[i][j] 表示 s1[:i-1] 和 s2[:j-1] 是否能构成 s3[:i+j-2] if n1 + n2 != n3: return False dp = [[False]*(n2+1) for _ in range(n1+1)] dp[0][0] = True for j in range(1, n2+1): dp[0][j] = dp[0][j-1] and s2[j-1] == s3[j-1] for i in range(1, n1+1): dp[i][0] = dp[i-1][0] and s1[i-1] == s3[i-1] for i in range(1, n1+1): for j in range(1, n2+1): dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] == s3[i+j-1]) return dp[-1][-1]
class Solution { public: bool isInterleave(string s1, string s2, string s3) { int n1 = s1.size(), n2 = s2.size(), n3 = s3.size(); if((n1 + n2) != n3) return false; // dp[i][j]: s1[:i] 和 s2[:j] 是否能组成s3[:i+j] vector<vector<bool>> dp(n1+1, vector<bool>(n2+1, false)); dp[0][0] = true; // s1 为空 for(int j{1}; j<n2+1; j++){ dp[0][j] = dp[0][j-1] && s2[j-1] == s3[j-1]; } // s2 为空 for(int i{1}; i<n1+1; i++){ dp[i][0] = dp[i-1][0] && s1[i-1] == s3[i-1]; } for(int i{1}; i<n1+1; i++){ for(int j{1}; j<n2+1; j++){ dp[i][j] = (dp[i-1][j] && s1[i-1] == s3[i+j-1]) || (dp[i][j-1] && s2[j-1] == s3[i+j-1]); } } return dp[n1][n2]; } };
119. 杨辉三角 II
你可以优化你的算法到 O(rowIndex)
空间复杂度吗?
class Solution { public: vector<int> getRow(int rowIndex) { // 已经经过路径压缩了 vector<int> res(rowIndex+1, 1); for(int i{1}; i<=rowIndex; i++){ for(int j{i-1}; j>0; j--){ // 每行的第一个元素和最后一个元素都是1 res[j] = res[j] + res[j-1]; } } return res; } };
120. 三角形最小路径和
class Solution { public: // 从上到下 int minimumTotal1(vector<vector<int>>& triangle) { int n = triangle.size(); // dp[i][j]:从(0,0)到(i,j)的最小路径和 // 如果是i==j, dp[i][j] = dp[i-1][j-1] + t[i][j] // 如果i != j, dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]) + t[i][j] vector<vector<int>> dp(n, vector<int>(n, 0)); dp[0][0] = triangle[0][0]; for(int i{1}; i<n; i++){ dp[i][0] = dp[i-1][0] + triangle[i][0]; } for(int i{1}; i<n; i++){ for(int j{1}; j<i+1; j++){ if(i == j){ dp[i][j] = dp[i-1][j-1] + triangle[i][j]; }else{ dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]; } } } return *min_element(dp[n-1].begin(), dp[n-1].end()); } // 从下到上 // dp[i][j] 表示从最后一行走到 (i,j)时的最小路径和 // dp[0][0] 记为需要返回的值 int minimumTotal2(vector<vector<int>>& triangle) { int n = triangle.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for(int j{0}; j < n; j++){ dp[n-1][j] = triangle[n-1][j]; } for(int i{n-2}; i>=0; i--){ for(int j{0}; j <=i; j++){ // 每个上一行元素,都必定有i+1,j 和 i+1,j+1 dp[i][j] = min(dp[i+1][j+1], dp[i+1][j]) + triangle[i][j]; } } return dp[0][0]; } // 从下到上,路径压缩 int minimumTotal(vector<vector<int>>& triangle) { int n = triangle.size(); vector<int> dp{triangle.back()}; for(int i{n-2}; i >= 0; i--){ for(int j{0}; j<=i; j++){ dp[j] = min(dp[j+1], dp[j]) + triangle[i][j]; } } return dp[0]; } };
1.分割等和子集
1 class Solution(object): 2 def canPartition(self, nums): 3 if(sum(nums)&1): 4 return False 5 6 target=sum(nums)//2 7 8 # dp[i]表示是否能从 nums中取出一些数字,其和为i 9 dp=[False]*(target+1) 10 11 dp[0]=True 12 13 for num in nums: 14 for i in range(target,num-1,-1): 15 dp[i]=dp[i] or dp[i-num] 16 17 return dp[target]
2.coins change i
组成amount的最少硬币数
1 class Solution(object): 2 def coinChange(self, coins, amount): 3 # dp[i]表示凑成金额i所需的最少硬币数 4 # dp[i]=min(dp[i],dp[i-coins[j]] if i-coins[j]>=0) 5 # dp[0]=0 6 7 dp=[float('inf') for j in range(amount+1)] 8 dp[0]=0 9 10 for j in range(1,amount+1): 11 for i in range(len(coins)): 12 if(j>=coins[i] and dp[j-coins[i]]!=float('inf')): 13 dp[j]=min(dp[j],dp[j-coins[i]]+1) 14 15 return dp[amount] if dp[amount]!=float('inf') else -1 16
3.1 coins change ii
组成amounts的所有可能数
1 class Solution(object): 2 def change(self, amount, coins): 3 m=len(coins) 4 n=amount 5 6 dp=[[0]*(n+1)]*(m+1) 7 dp[0][0]=1 8 9 # 下面写法错误,没有任何硬币,所以无法组成 10 # for j in range(n+1): 11 # if j %coins[0]==0: 12 # dp[0][j]=1 13 14 for i in range(1,m+1): 15 dp[i][0]=1 16 17 for i in range(1,m+1): 18 for j in range(1,n+1): 19 dp[i][j]=dp[i-1][j]+(dp[i][j-coins[i-1]] if j-coins[i-1]>=0 else 0) 20 21 return dp[m][n]
def change(self, amount, coins): m=len(coins) n=amount # dp[i]表示组成i的组合数 dp=[0]*(n+1) dp[0]=1 for i in range(m): for j in range(coins[i],n+1): dp[j]=dp[j]+dp[j-coins[i]] return dp[amount]
3.2 coins change i
组成amount所需的最少硬币数
1 class Solution(object): 2 def coinChange(self, coins, amount): 3 """ 4 :type coins: List[int] 5 :type amount: int 6 :rtype: int 7 """ 8 9 m=len(coins) 10 n=amount 11 12 # dp[j]表示组成amount j所需的最少硬币数 13 dp=[float('inf') for j in range(n+1)] 14 15 dp[0]=0 16 for j in range(1,n+1): 17 for i in range(m): 18 if(j>=coins[i]): 19 dp[j]=min(dp[j],dp[j-coins[i]]+1) 20 21 return dp[n] if dp[n]!=float('inf') else -1
4. 打家劫舍
1 class Solution(object): 2 def helper(self,nums): 3 # dp[i]表示偷 前i个房子时 最高的金额数 4 dp=[0]*len(nums) 5 dp[0]=nums[0] 6 7 for i in range(1,len(nums)): 8 dp[i]=max(dp[i-1],nums[i]+(dp[i-2] if i-2>=0 else 0)) 9 10 return dp[-1] 11 12 def rob(self, nums): 13 if(len(nums)==0): 14 return 0 15 16 if(len(nums)==1): 17 return nums[0] 18 19 if(len(nums)==2): 20 return max(nums[0],nums[1]) 21 22 nums1=nums[:-1] 23 nums2=nums[1:] 24 25 return max(self.helper(nums1),self.helper(nums2))
5.LIS
1 class Solution(object): 2 def lengthOfLIS(self, nums): 3 n=len(nums) 4 if(n==0 or n==1): 5 return n 6 7 dp=[1]*n 8 9 for i in range(1,n): 10 for j in range(i): 11 if(nums[j]<nums[i]): 12 dp[i]=max(dp[j]+1,dp[i]) 13 14 return max(dp)
6.最大和连续子数组
1 class Solution(object): 2 def maxSubArray(self, nums): 3 n=len(nums) 4 if(n==0): 5 return 0 6 7 curSum=0 8 maxSum=float('-inf') 9 10 for i in range(n): 11 curSum+=nums[i] 12 maxSum=max(curSum,maxSum) 13 14 if(curSum<0): 15 curSum=0 16 17 18 return maxSum
7.众数
1 class Solution(object): 2 def majorityElement(self, nums): 3 n=len(nums) 4 if(n==0): 5 return [] 6 7 if(n==1): 8 return nums 9 10 res=[] 11 12 num1=float('inf') 13 num2=float('inf') 14 cnt1=0 15 cnt2=0 16 17 for i in range(n): 18 if(nums[i]==num1): 19 cnt1+=1 20 elif(nums[i]==num2): 21 cnt2+=1 22 elif(cnt1==0): 23 num1=nums[i] 24 cnt1=1 25 elif(cnt2==0): 26 num2=nums[i] 27 cnt2=1 28 else: 29 cnt1-=1 30 cnt2-=1 31 32 cnt1,cnt2=0,0 33 for num in nums: 34 if(num==num1): 35 cnt1+=1 36 elif(num==num2): 37 cnt2+=1 38 39 if(cnt1>n//3): 40 res.append(num1) 41 if(cnt2>n//3): 42 res.append(num2) 43 44 return res
误区1:
num1和num2初始化为nums[0]。再执行循环。
假设数组中只有两个不相同的元素,那么结束循环时,num1和num2仍指向同一个元素
91解码方式
class Solution: def numDecodings(self, s: str) -> int: # dp[i]: s[:i] (不含i)可以解码的次数 # dp[i] += dp[i-1] if s[i-1]是合法的 # dp[i] += dp[i-2] if s[i-2:i]是合法的 n = len(s) if n == 1: if s == "0": return 0 return 1 dp = [0]*(n+1) dp[0] = 1 for i in range(1, n+1): if 0 < int(s[i-1]) < 10: dp[i] += dp[i-1] if i > 1 and 10 <= (int(s[i-2])*10 + int(s[i-1])) <= 26: dp[i] += dp[i-2] return dp[n]
def numDecodings(self, s: str) -> int: n = len(s) if n == 1: if s == "0": return 0 return 1 pprev, prev = 1, 1 for i in range(1, n+1): cur = 0 if 1 <= int(s[i-1]) < 10: cur += prev if i > 1 and 10 <= int(s[i-2:i]) <= 26: cur += pprev pprev, prev = prev, cur return cur