1.股票买卖
121. 买卖股票的最佳时机 - 力扣(LeetCode) (leetcode-cn.com)
可以用大小为2的dp数组,第一个表示买进最低价格获得收益,第二个表示卖出最高价格获得收益
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#dp[0] 为买入 价格负值的最大 买进最低价格 此时最大收益
#dp[1] 为卖出 价格正值的最大 卖出最高价格 此时最大收益
dp = [-prices[0],0]
for i in prices:
dp[0] = max(dp[0],-i) #买进 i越小越好 -i越大越好
dp[1] = max(dp[1], dp[0]+i)
return dp[1]
122. 买卖股票的最佳时机 II 题解 - 力扣(LeetCode) (leetcode-cn.com)
可以重复卖出买进 那么就需要把买进加上卖出最大收益
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#用dp数组2个 第一个表示买进最大收益 第2个表示卖出最大收益
#可以重复买进卖出 每次计算买进收益就要叠加卖出收益
dp= [-prices[0],0]
for i in prices:
dp[0] = max(dp[0],dp[1]-i)
dp[1] = max(dp[1],dp[0]+i)
#print(dp[1])
return dp[1]
123. 买卖股票的最佳时机 III - 力扣(LeetCode) (leetcode-cn.com)
这道题限制最大交易两次,相当于要把上一道题的环解开
dp[0] = max(dp[0],dp[1]-i)
dp[1] = max(dp[1],dp[0]+i)
要变成
dp[0] = max(dp[0], 0 -i)
dp[1] = max(dp[1],dp[0]+i)
dp[2] = max(dp[2],dp[1] -i)
dp[3] = max(dp[3],dp[2]+i)
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#动态规划
#dp数组长度为4 分别为第一次买进 第一次卖出 第二次买进 第二次卖出 最大收益
dp = [-prices[0], 0, -prices[0], 0]
for i in prices:
dp[0] = max( -i, dp[0])
dp[1] = max(dp[0]+i, dp[1])
dp[2] = max(dp[1]-i, dp[2])
dp[3] = max(dp[2]+i, dp[3])
return dp[3]
188. 买卖股票的最佳时机 IV - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
if not prices or not k:return 0
dp= [-prices[0], 0]*k
for i in prices:
for j in range(len(dp)):
if not j%2:
if not j : dp[j] = max(-i,dp[j])
else: dp[j] = max(dp[j-1]-i,dp[j])
else: dp[j] = max(dp[j-1]+i,dp[j])
return dp[-1]
188. 买卖股票的最佳时机 IV - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n == 0:return 0
dp = [[0] * 3 for _ in range(n)]
dp[0][0] = -prices[0] #持股票
for i in range(1, n):
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][2])
dp[i][2] = dp[i-1][0] + prices[i]
return max(dp[n-1][1], dp[n-1][2])
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = [-prices[0],0,0]
for i in prices:
dp = max(dp[0],dp[1]-i), max(dp[1],dp[2]), dp[0]+i
#print(dp)
return max(dp[1:])
714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
#动态规划
#dp表示买入获的利润最大和卖出获得利润最大
dp= [-prices[0], 0]
for i in prices:
dp[0] = max(dp[1]-i, dp[0]) #买入
dp[1] = max(dp[0]+i-fee, dp[1]) #卖出
return dp[1]
2.数组或序列
300. 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
#dp[i]为第i个字符串最长递增长度
dp = [1]*len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[i]> nums[j]:
dp[i] =max(dp[j]+1,dp[i])
#print(dp)
return max(dp)
674. 最长连续递增序列 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
#dp[i]此位最大序列长度 注意是连续的
#dp[i]=dp[i-1]+1 if nums[i]>nums[i-1]
dp = [1] * len(nums)
for i in range(1,len(nums)):
if nums[i] > nums[i-1]:dp[i] =dp[i-1]+1
#print(dp)
return max(dp)
718. 最长重复子数组 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
#动态规划 滚动数组
dp = [0] * (len(B) + 1)
res = 0
for i in range(len(A)): #物品
for j in range(len(B)-1, -1, -1):#背包
if not A[i] - B[j]:
dp[j+1] = dp[j] + 1
else:
dp[j+1] = 0 #注意这里不相等的时候要有赋0的操作
res = max(res, dp[j+1])
return res
1143. 最长公共子序列 - 力扣(LeetCode) (leetcode-cn.com)
我对自己服气了,前面办法没用了,只能用这个二维数组了
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
len1, len2 = len(text1)+1, len(text2)+1
dp = [[0 for _ in range(len1)] for _ in range(len2)] # 先对dp数组做初始化操作
for i in range(1, len2):
for j in range(1, len1): # 开始列出状态转移方程
if text1[j-1] == text2[i-1]:
dp[i][j] = dp[i-1][j-1]+1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[-1][-1]
1035. 不相交的线 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
#和最长重复子集一样
dp= [[0]*(len(nums1)+1) for _ in range(len(nums2)+1)]
for i in range(1,len(nums2)+1):
for j in range(1,len(nums1)+1):
if nums1[j-1] == nums2[i-1]:
dp[i][j] = dp[i-1][j-1]+1
else:dp[i][j] =max(dp[i-1][j], dp[i][j-1])
return dp[-1][-1]
53. 最大子数组和 - 力扣(LeetCode) (leetcode-cn.com)
这道题在贪心时候算过,现在改用动态规划再做一次
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
#动态规划
#dp[i] 第i个元素前面最大和
#dp[i]= max(nums[i],nums[i]+dp[i-1])
dp= [0]*len(nums)
for i in range(len(nums)):
dp[i]= max(nums[i], nums[i]+dp[i-1])
#print(dp)
return max(dp)
115. 不同的子序列 - 力扣(LeetCode) (leetcode-cn.com)
class Solution:
def numDistinct(self, s: str, t: str) -> int:
#dp[i][j] 表示s的下标为i-1子序列中出现以 t的下标为j-1 的个数
dp = [[1]+[0]*len(t) for _ in range(len(s)+1)]
for i in range(1,len(s)+1):
for j in range(1,len(t)+1):
if t[j-1]==s[i-1]: dp[i][j] = dp[i-1][j] +dp[i-1][j-1]
else: dp[i][j] = dp[i-1][j]
#print(dp)
return dp[-1][-1]
class Solution:
def numDistinct(self, s: str, t: str) -> int:
#dp[i][j] 表示s的下标为i-1子序列中出现以 t的下标为j-1 的个数
"""
既然dp[i]只用到dp[i - 1]的状态,
我们可以通过缓存dp[i - 1]的状态来对dp进行压缩,
减少空间复杂度。
(原理等同同于滚动数组)
"""
dp = [1]+[0]*len(t)
for i in range(1,len(s)+1):
prev = dp.copy()
for j in range(1,len(t)+1):
if t[j-1]==s[i-1]: dp[j] = prev[j] +prev[j-1]
else: dp[j] = prev[j]
#print(dp)
return dp[-1]
583. 两个字符串的删除操作 - 力扣(LeetCode) (leetcode-cn.com)
可以先找出重复的个数,然后用两个字符串加起来的长度减去它
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
#计算重复字符串的个数
#dp[i][j] 表示word1下标为i word2 下标j 重复字符串的个数
dp = [[0]*(len(word1)+1) for _ in range(len(word2)+1)]
for i in range(1,len(word2)+1):
for j in range(1,len(word1)+1):
if word2[i-1]== word1[j-1]: dp[i][j]=dp[i-1][j-1]+1
else: dp[i][j]= max(dp[i-1][j],dp[i][j-1])
#print(dp)
return len(word1)+len(word2)-2*dp[-1][-1]
可以通过滚动数组修改
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
#计算重复字符串的个数 滚动数组
#dp[i][j] 表示word1下标为i word2 下标j 重复字符串的个数
dp = [0]*(len(word1)+1)
for i in range(1,len(word2)+1):
prev = dp[:] #滚动数组-引入参数
for j in range(1,len(word1)+1):
if word2[i-1]== word1[j-1]: dp[j]=prev[j-1]+1
else: dp[j]= max(prev[j],dp[j-1])
#print(dp)
return len(word1)+len(word2)-2*dp[-1]
3.编辑距离
72. 编辑距离 - 力扣(LeetCode) (leetcode-cn.com)
需要考虑的东西太多了,看完答案才明白它什么意思。
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
#dp[i][j] 表示word1 转化为 下标为iword2下标为j 最少的操作数
dp = [[0]*(len(word2)+1) for _ in range(len(word1)+1)]
dp[0]=[_ for _ in range(len(word2)+1)]#dp[i][0]就应该是i,对word1全部做删除操作,即:dp[i][0] = i;
for i in range(len(word1)+1): dp[i][0]=i
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 #前两个为 插入和删除 最后一个为替换
#print(dp)
return dp[-1][-1]
4.回文子串
647. 回文子串 - 力扣(LeetCode) (leetcode-cn.com)
暴力解法
class Solution:
def countSubstrings(self, s: str) -> int:
res=0
for start in range(len(s)):
for ender in range(start+1,len(s)+1):
prev = s[start:ender]
if prev == prev[::-1]:res+=1
return res
双指针法
class Solution:
def countSubstrings(self, s: str) -> int:
def extend(i, j):
res = 0
while i >= 0 and j < len(s) and s[i] == s[j]:
i -= 1
j += 1
res += 1
return res
res = 0
for i in range(len(s)):
res += extend(i, i) #以i为中心
res += extend(i, i+1) #以i和i+1为中心
return res
动态规划
class Solution:
def countSubstrings(self, s: str) -> int:
'''
情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串
情况二:下标i 与 j相差为1,例如aa,也是文子串
情况三:下标:i 与 j相差大于1的时候,
例如cabac,此时s[i]与s[j]已经相同了,我们看i到j区间是不是回文子串就看aba是不是回文就可以了,
那么aba的区间就是 i+1 与 j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true。
'''
dp = [[False] * len(s) for _ in range(len(s))]
res = 0
for i in range(len(s)-1, -1, -1): #注意遍历顺序
for j in range(i, len(s)): #要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的
if s[i] == s[j]:
if j - i <= 1: #情况一 和 情况二
res += 1
dp[i][j] = True
elif dp[i+1][j-1]: #情况三
res += 1
dp[i][j] = True
return res
516. 最长回文子序列 - 力扣(LeetCode) (leetcode-cn.com)
dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]

class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
#dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]
dp = [[0] * len(s) for _ in range(len(s))]
for i in range(len(s)):dp[i][i] = 1
for i in range(len(s)-1, -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])
#print(dp)
return dp[0][-1]
5.总结
好歹全部过了一遍,后面有些题,下次遇见还是要再去研究下的。
好吧,动态规划算是完成了,
还有几道单调栈的题,晚上再做把,歇息一会了。
如果单调栈做完,系统刷题就做完了。