力扣刷题-python-动态规划(完结)-3(股票买卖、数组或序列、编辑距离、回文子串)

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.总结

好歹全部过了一遍,后面有些题,下次遇见还是要再去研究下的。
好吧,动态规划算是完成了,
还有几道单调栈的题,晚上再做把,歇息一会了。
如果单调栈做完,系统刷题就做完了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值