动态规划2


背包问题

2021/1/26

416. 分割等和子集

如果不定义第一列为true,就要对于每个i元素,赋值dp[i][nums[i]]=True

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if(len(nums)<2):
            return False
        # 求sum/2
        numSum=0
        for num in nums:
            numSum+=num
        # 奇数
        if(numSum%2!=0):
            return False
        # 偶数
        else:
            numSum=int(numSum/2)
        # dp[i][j] 表示前[0,i]个元素中能否取出一些元素sum=j
        dp=[[False]*(numSum+1) for _ in range(len(nums))]
        
        for i in range(len(nums)):
            if(nums[i]<=numSum):
                dp[i][nums[i]]=True
            for j in range(1,numSum+1):
                # S1:不拿i元素,dp[i-1][j]为true才可以
                # S2:拿i元素,dp[i-1][j-元素i],为true才可以
                # 不拿i元素
                if(i>0):
                    dp[i][j]=dp[i-1][j] or dp[i][j]
                # 拿i元素 由前提 j-nums[i]>=0
                if(dp[i][j]==False and j-nums[i]>=0):
                    dp[i][j]=dp[i-1][j-nums[i]] 
                # 提前终止
                if(dp[i][numSum]==True):
                    #print(dp)
                    return True
        # 否则
        #print(dp)
        return False
  • 直接命令第一列为0,后面就不用赋值了
  • nums[i],分<=j,和>j两种情况考虑
  • 先初始化第一行,然后每一行结果由上一行结果和nums[i]决定
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if(len(nums)<2):
            return False
        # 求sum/2
        numSum=0
        for num in nums:
            numSum+=num
        # 奇数
        if(numSum%2!=0):
            return False
        # 偶数
        else:
            numSum=int(numSum/2)
        # dp[i][j] 表示前[0,i]个元素中能否取出一些元素sum=j
        dp=[[False]*(numSum+1) for _ in range(len(nums))]
        # 初始化第一个元素的结果(第一行) 每一行的结果由上一行决定
        dp[0][0]=True
        if(nums[0]<=numSum):
            dp[0]
        for i in range(1,len(nums)):
            dp[i][0]=True # 令第一列为true
            for j in range(1,numSum+1):
                # S1:不拿i元素,dp[i-1][j]为true才可以
                # S2:拿i元素,dp[i-1][j-元素i],为true才可以

                #元素i小于j,可拿可不拿i
                if(nums[i]<=j):
                    dp[i][j]=dp[i-1][j-nums[i]] or dp[i-1][j]
                #元素i大于j,不能拿i
                else:
                    dp[i][j]=dp[i-1][j]
                # 提前终止
                if(dp[i][numSum]==True):
                    #print(dp)
                    return True
        # 否则
        #print(dp)
        return False

优化空间复杂度

  • 每一行的dp只与上一行的dp相关
  • ==dp[i]表示抽出一些元素 sum等于i ==
  • 遍历Nums:
    • num>target 不用管
    • num<=target 更新dp
      • 只更新dp[target]到dp[num](因为dp[0]—dp[num-1]即sum,都比当前num小,当前num管不着它们的更新)
      • 必须先更新大的,再更新小的,即倒序更新dp[i]
      • dp[i]=dp[i] or dp[j-num] dp[i]等于当前状态,或者不取当前num时的dp[j-num]
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if(len(nums)<2):
            return False
        # 求sum/2
        numSum=0
        for num in nums:
            numSum+=num
        # 奇数
        if(numSum%2!=0):
            return False
        # 偶数
        else:
            numSum=numSum//2
        # dp[j]表示能否抽出部分数,sum=j
        dp=[True]+[False]*numSum
        for num in nums:
            if(num>numSum):
                continue
            #当num<=target时 判断
            for j in range(numSum,num-1,-1):
                dp[j]=dp[j] or dp[j-num]
            # 提前终止
            if(dp[numSum]==True):
                return True

        return dp[numSum]

2021/1/28

494. 目标和

dp[i][j]表示前i个元素sum为j的次数

转移关系

  • S1:当前num+,dp[i][j]+=dp[i-1][j-num]
  • S2:当前num-,dp[i][j]+=dp[i-1][j+num]
class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        numSum=0
        for num in nums:
            numSum+=num
        if(S>numSum or S<-numSum):
            return 0
        # dp[i][j]表示前i+1个元素,组合sum=j的次数
        dp=[[0]*(2*numSum+1) for _ in range(0,len(nums))]
        # 转移关系
        # S1:当前num+,dp[i][j]+=dp[i-1][j-num]
        # S2:当前num-,dp[i][j]+=dp[i-1][j+num]

        # 初始化第一行 numSum相当于sum=0的index
        dp[0][numSum+nums[0]]+=1 # dp[numSum[0]]=0 
        dp[0][numSum-nums[0]]+=1 # 如果直接等于的话 第一个元素为0时,只会令其=1,实际=2,
        for i in range(1,len(nums)):          
            for j in range(0,2*numSum+1):
                # target=j-numSum #将j从index转化到target sum
                # dp[i][j]+=dp[i-1][target-nums[i]+numSum]
                # dp[i][j]+=dp[i-1][target+nums[i]+numSum]
                if(j-nums[i]>=0 and j-nums[i]<=2*numSum):
                    dp[i][j]+=dp[i-1][j-nums[i]]
                if(j+nums[i]>=0 and j+nums[i]<=2*numSum):
                    dp[i][j]+=dp[i-1][j+nums[i]]
        #print(dp)
        return dp[len(nums)-1][S+numSum]

优化空间复杂度,因为cur可以由左边+num得到,也可以由右边-num得到,所以需要两个一维数组

  • Q:为什么 cur=L+R,不需要L+R+pre 呢??
class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        numSum=0
        for num in nums:
            numSum+=num
        if(S>numSum or S<-numSum):
            return 0
        # 空间复杂度优化
        # dp[i]表示sum=i-numSum的次数
        pre=[0]*(2*numSum+1)
        # 初始化第一个元素
        pre[numSum+nums[0]]+=1
        pre[numSum-nums[0]]+=1
        cur=[0]*(2*numSum+1)
        # 转移关系
        # S1:当前num+,dp[j]+=dp[j-num]
        # S2:当前num-,dp[j]+=[j+num]
        for num in nums[1::]:
            for j in range(0,2*numSum+1):
                L=0 # 从左边+num 要倒叙
                R=0 # 从右边-num 要正序
                if(j-num>=0 and j-num<=2*numSum):
                    L=pre[j-num]
                if(j+num>=0 and j+num<=2*numSum):
                    R=pre[j+num]
                #cur[j]=pre[j]+L+R #一直卡在这一条上了
                cur[j]=L+R # cur为什么不用+pre呢??
            
            pre=cur[::]

        return pre[S+numSum]

大神解法,转化成subsum问题,真心厉害了。。

474. 一和零

两个背包,定义一个两个对应容量的dp[m][n]
dp[m][n]表示m个0字符,n个1字符容量下最大value,本题中即为str个数
对于每个str:

  • ①拿当前str, dp[i][j]= dp[i][j]
  • ②不拿当前str, dp[i][j]= dp[i-当前0个数][j-当前1个数]+1
  • 实际 dp[i][j]取上述两种情况最大值

关键点:注意背包问题中倒叙更新

  • 后面项由前面项决定 如果正序的话 会累加
  • 比如 dp[3]=dp[2]+1 dp[2]=dp[1]+1 dp[1]=dp[0]+1 倒叙没问题,用的dp[i]都是本轮的值
  • dp[1]=dp[0]+1 dp[2]=dp[1]+1 dp[3]=dp[2]+1 正序的话,单独看每个式子没问题,但是连着看,就变成累加了 dp[2]更新中的dp[1]是已经更新后的dp[1],不是本轮的值
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        # m+1行 n+1列 dp[i][j]表示 m=i n=j下的最大value(即元素数)
        dp=[[0]*(n+1) for _ in range(m+1)]
        for s in strs:
            num0,num1=self.getCharNum(s)
            # 其实下面两行不加也Ok
            if(num0>m or num1>n):
                continue
            # 注意要倒叙排列
            # 后面项由前面项决定 如果正序的话 会累加 
            for i in range(m,num0-1,-1):
                for j in range(n,num1-1,-1): 
                    # S1:不拿s;S2:拿s
                    dp[i][j]=max(dp[i][j],dp[i-num0][j-num1]+1)
        return dp[m][n]

    # 获取str中0和1的个数
    def getCharNum(self,s):
        num0=0
        num1=0
        for char in s:
            if(char=='1'):
                num1+=1
            elif(char=='0'):
                num0+=1
        return num0,num1

2021/1/29

322. 零钱兑换
  • dp[i]表示总金额i 最少硬币数量
  • dp[i]只能由最小的dp[i-coin]+1得到
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if(amount<0):
            return -1
        if(amount==0):
            return 0
        
        # dp[i]表示总金额i 最少硬币数量
        dp=[0]*(amount+1)
        
        # F(i)=min[F(i-coin1),F(i-coin2,...)]+1
        for i in range(1,amount+1):
            # 遍历coin
            for coin in coins:
                if(coin==i):
                    dp[i]=1
                if(coin<i and dp[i-coin]>0):
                    if(dp[i]==0):
                        dp[i]=dp[i-coin]+1
                    else:
                        dp[i]=min(dp[i],dp[i-coin]+1)
        #print(dp)
        return dp[amount] if dp[amount]>0 else -1

官方给的,初始化dp为正无穷,dp[0]=0

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if(amount<0):
            return -1
        
        # dp[i]表示总金额i 最少硬币数量
        dp=[float('inf')]*(amount+1) #正无穷
        dp[0]=0

        # F(i)=min[F(i-coin1),F(i-coin2,...)]+1
        for i in range(1,amount+1):
            # 遍历coin
            minPre=float('inf')
            for coin in coins:
                if(coin<=i):
                    minPre=min(minPre,dp[i-coin])
            dp[i]=min(dp[i],minPre+1)
        #print(dp)
        return dp[amount] if dp[amount]!=float('inf') else -1

2021/3/1

518. 零钱兑换 II
  • 先遍历所有的硬币,对于每一个硬币,更新dp[i],用dp[i-coin]来更新dp[i]
class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        # 遍历所有coin 对于每一个coin 更新dp[i]
        dp=[0]*(amount+1)
        dp[0]=1
        for coin in coins:
            for i in range(coin,amount+1):
                dp[i]+=dp[i-coin]
        return dp[amount]

思路dp[i]+=dp[i-coin]想到了,但是误以为要遍历 0-amount,用dp[i-coin]更新dp[i],这样得到的结果会偏大,因为可能会有重复的组合。该思路计算的结果是排列数,而不是组合数,也就是代码会把1,2和2,1当做两种情况。但更加根本的原因是子问题定义出现了错误。
在这里插入图片描述

309. 最佳买卖股票时机含冷冻期
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if(len(prices)<=1):
            return 0
        # 0 持有
        # 1 不持有,处于冷冻期
        # 2 不持有,不处于冷冻期
        dp=[[0]*3 for _ in range(0,len(prices))]
        for i in range(0,len(prices)):
            if(i==0):
                dp[0][0]=-prices[0]
            else:
                dp[i][0]=max(dp[i-1][2]-prices[i],dp[i-1][0]) # 持有,①可以是买入;②也可以继续持有
                dp[i][1]=dp[i-1][0]+prices[i] # 处于冷冻期 只能前一天持有 现在卖出
                dp[i][2]=max(dp[i-1][1],dp[i-1][2]) # ①前一天冷冻;②前一天不冷动
        return max(dp[-1][1],dp[-1][2])

优化空间复杂度,因为dp[i][]只和dp[i-1][]相关

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if(len(prices)<=1):
            return 0
        # s0 持有
        # s1 不持有,处于冷冻期
        # s2 不持有,不处于冷冻期
        dp=[0]*len(prices)
        s0=-prices[0]
        s1=0
        s2=0
        for i in range(1,len(prices)):
            s0_=max(s0,s2-prices[i])
            s1_=s0+prices[i]
            s2_=max(s1,s2)
            s0,s1,s2=s0_,s1_,s2_
            dp[i]=max(s1,s2)

        return dp[i]

2021/3/3

139. 单词拆分

前[i+1]个字符子串判断时:

  • 前0个字符 i+1字符
  • 前1个字符 后i个字符

leetcode比如判断leetco子串时:

  • /leetco
  • l/eetco
  • le/etco
  • lee/tco
  • leet/co
  • leetc/o
class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp=[False]*(len(s)+1)
        dp[0]=True # dp[i]表示前i个字符子串能否被dict拆分
        # 前i个字符
        # 前i个字符+1个字符
        for i in range(1,len(s)+1):
            for j in range(0,i):
                if(dp[j] and s[j:i] in wordDict):
                    dp[i]=True
                    break

        return dp[-1]
377. 组合总和 Ⅳ
class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        dp=[0]*(target+1)
        dp[0]=1
        for i in range(1,target+1):
            for num in nums:
                if(num<=i):
                    dp[i]+=dp[i-num]
        return dp[target]
714. 买卖股票的最佳时机含手续费

可以优化空间复杂度

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        # 0有股票
        # 1无股票
        if(len(prices)<=1):
            return 0
        # dp=[[0]*2 for i in range(0,len(prices))]
        # dp[0][0]=-prices[0]-fee
        # for i in range(1,len(prices)):
        #     # 有股票:(1)继续持有;(2)i-1无股票,i买入
        #     dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]-fee) 
        #     # 无股票;(1)基于无股票;(2)i-1有股票,i卖出
        #     dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
        # return dp[-1][1]

        no=0 # 无股票
        yes=-prices[0]-fee # 有股票
        for i in range(1,len(prices)):
            # 无股票 两种情况
            no_new=max(no,yes+prices[i])
            # 有股票 两者情况
            yes_new=max(yes,no-prices[i]-fee)
            no,yes=no_new,yes_new

        return no

2021/3/9

123. 买卖股票的最佳时机 III

注意两个地方:

  • Q1:为什么buy2也用-prices[0]初始化
  • Q2:一次循环的时候 buy1,sale1,buy2,sale2直接修改 不用临时变量存储
    • 是因为可以看作当天买入 当天卖出 sale1=0 ;不太理解,是不是相当于 当prices[2]低于prices[1]时,在t=2时刻,buy1会更新为-prices[2],而sale1更新为0,(如果按照之前自己的想法,slae2=prices[2]-price[1]),他这种强制为0,并不是指一定要完成1次买入和卖出;还是不太理解。
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if(len(prices)<2):
            return 0
        buy1=-prices[0]
        sale1=0
        buy2=-prices[0]
        sale2=0

        # Q1:为什么buy2也用-prices[0]初始化
        # Q2:一次循环的时候 buy1,sale1,buy2,sale2直接修改 不用临时变量存储
        for i in range(1,len(prices)):
            if(prices[i]<-buy1):
                buy1=-prices[i]
            
            if(prices[i]+buy1>sale1):
                sale1=prices[i]+buy1

            if(sale1-prices[i]>buy2):
                buy2=sale1-prices[i]

            if(prices[i]+buy2>sale2):
                sale2=prices[i]+buy2

        return max(0,sale1,sale2)

官方给出:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        buy1 = buy2 = -prices[0]
        sell1 = sell2 = 0
        for i in range(1, n):
            buy1 = max(buy1, -prices[i])
            sell1 = max(sell1, buy1 + prices[i])
            buy2 = max(buy2, sell1 - prices[i])
            sell2 = max(sell2, buy2 + prices[i])
        return sell2
583. 两个字符串的删除操作

和求最大子序列相同,想到思路即可,关键是判断尾字符,以及不同时取前两个的最大值

  • 当两个尾字符相等时,
  • 当两个尾字符不等时,
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        dp=[[0]*(len(word2)+1) for _ in range(0,len(word1)+1)]

        for i in range(0,len(word1)):
            for j in range(0,len(word2)):
                if(word1[i]==word2[j]):
                    dp[i+1][j+1]=dp[i][j]+1
                else:
                    dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1])

        # print(dp)
        return len(word1)+len(word2)-2*dp[len(word1)][len(word2)]

2021/3/12

72. 编辑距离

假设w[i]表示第i个单词

  • w1[i]==w2[j]:不需要额外操作dp[i][j]=dp[i-1][j-1]
  • w1[i]!=w2[j]:
    • 删掉w1的i字符:dp[i-1][j] +删除
    • 替换w1的i字符:dp[i-1][j-1]+替换
    • 插入字符:只可能在i字符之后插入,dp[i][j-1]+插入
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # dp[i][j] 表示w1前i个字符转化成w2前j个字符 最小操作数
        len1,len2=len(word1),len(word2)
        dp=[[0]*(len2+1) for _ in range(0,len1+1)]
        # 初始化0行 0列
        dp[0]=list(range(0,len2+1))
        for i in range(1,len1+1):
            dp[i][0]=i
        # 开始循环
        for i in range(1,len1+1):
            for j in range(1,len2+1):
                # S1:w1[i]=w2[j]
                if(word1[i-1]==word2[j-1]):
                    # 不需增加操作
                    dp[i][j]=dp[i-1][j-1]
                # S2: w1[i]!=w2[j]
                else:
                    # 选出 删除、替换、插入的最小值
                    dp[i][j]=min(dp[i-1][j]+1,dp[i-1][j-1]+1,dp[i][j-1]+1)

        #print(dp)
        return dp[len1][len2]

2021/4/10

真的是2个月没刷题了。。

650. 只有两个键的键盘

dp[i][j]表示 target=i,最后由j作为被粘贴的数 得到的操作数
获得当前dp[i][j]后,两种情况

  • s1:继续粘贴上一次的num;得到i+j
  • s2:复制当前的数并且粘贴;得到2*i
    但是代码速度很慢,是因为一个数能整除2的情况下,一定由复制得到的操作数小于复制1得到情况;
    自己的代码相当于把所有情况弄出来了,但实际上只需要考虑最有效的路径
class Solution:
    def minSteps(self, n: int) -> int:
        if(n==1):
            return 0
        import sys
        # 想法:dp[i][j]表示 target=i,最后由j作为被粘贴的数 得到的操作数
        # 实际:dp[i][j] target=i+1 粘贴的数是j+1 所以dp的size可以加一行一列
        #dp=[[sys.maxsize]*(n//2+1) for _ in range(n+1)]
        dp=[[float('inf')]*(n//2+1) for _ in range(n+1)]
        dp[1][1]=1
        for i in range(1,n):
            for j in range(1,n//2+1):
                # s1:继续粘贴上一次的num
                if(i+j<=n):
                    dp[i+j][j]=min(dp[i+j][j],dp[i][j]+1)
                # s2:复制当前的数并且粘贴
                if(2*i<=n):
                    dp[2*i][i]=min(dp[2*i][i],dp[i][j]+2)
        #print(dp)  
        return min(dp[n])

官方题解,转化成数学题。。。。。

2021/7/23

97. 交错字符串

这种字符串的dp,很可能就是s1 s2分别做行列,然后可以使用滚动数组降低空间复杂度

class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        n=len(s1)
        m=len(s2)
        if(n+m!=len(s3)):
            return False
        # dp[i][j] s1前i个 和 s2前j个 能否构成s3 前i+j个
        dp=[False]*(m+1)
        dp[0]=True
        # dp[i][j]取决于 dp[i-1][j] s[j]和s1[i]
        # dp[i][j] 表示s1前i个 所以是i+1 
        for j in range(1,m+1):
            dp[j]=dp[j-1] and s2[j-1]==s3[j-1]

        for i in range(1,n+1):
            # 注意滚动数组:不能忘记只使用s1的情况
            dp[0]=dp[0] and s1[i-1]==s3[i-1]
            for j in range(1,m+1):
                dp[j]=(dp[j] and s1[i-1]==s3[i+j-1]) or (dp[j-1] and s2[j-1]==s3[i+j-1])

        return dp[m]
115. 不同的子序列

dp[i][j]表示【s前i个元素】中有【t前j个元素】的【子串数量】,这题状态更新还是容易乱,注意的点:

  • 关于二维dp矩阵,为了统一,就直接弄成m+1行,n+1列(可以弄成m行 但是s和t的下标就不统一写法了);默认dp[0][0]=1
  • 更新dp[i][\j]时:
    • s[i-1]!=t[j-1]时(m+1,n+1这里下标就统一了),dp[i][j]=dp[i-1][j] (这个很好理解s变长了,并且新增的元素不等于t[j-1])
    • s[i-1]t[j-1]时,说明s新增的元素等于t[j-1],即子串的最后一个字符,那么又分成两种情况:①还是不用这个新增的元素s[i-1],还是原来的结果dp[\i-1][j] ②用这个新增的元素作为字串的最后一个元素,结果就是用dp[i]中含字串[:-1]的结果来匹配上当前新增元素==;将①②结果相加即为结果
  • 改成滚动数组,降低空间复杂度
class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        m=len(s)
        n=len(t)
        if(n>m or n==0):
            return 0
        # dp[i][j]表示【s前i个元素】中有【t前j个元素】的【子串数量】
        # 默认dp[0][0]=1
        # dp[i][j]=dp[i-1][j] if s[i-1]!=t[j-1] else dp[i-1][j]+dp[i-1][j-1]
        dp=[0]*(n+1) # 补0是为了dp[j-1]
        dp[0]=1 # 补0是为了不用+1
        for i in range(1,m+1):
            dp_new=[0]*(n+1)
            dp_new[0]=1
            for j in range(1,n+1):
                dp_new[j]=dp[j] if s[i-1]!=t[j-1] else dp[j]+dp[j-1]
            dp=dp_new

        return dp[n]

2021/9/13

132. 分割回文串 II

分为两步:

  • 1.计算所有的可能回文idx dp[i][j]
  • 2.使用cut[k]表示前k+1个字符串分割最小的次数 然后新增s[k]时,需要考虑0,1,2,…k-1的情况

时间复杂度 O(n^2)

class Solution:
    def minCut(self, s: str) -> int:
        # dp[i][j]表示 s[i:j+1]是否回文
        # cut[k] 表示s[0:k+1]最少的次数
        dp=[[True]*len(s) for _ in range(len(s))]
        cut=[len(s)]*len(s)
        cut[0]=0

        # 回文dp[i][j]=(s[i]==s[j]) dp[i+1][j-1]
        for i in range(len(s)-1,-1,-1):
            for j in range(i+1,len(s)):
                dp[i][j]=(s[i]==s[j]) and dp[i+1][j-1]
        
        # 新增s[i]对应cut[i]
        for i in range(1,len(s)):
            # s[i]可以和s[i-1] s[i-2]..s[0]比较
            # 0 1 idx ... i
            if(dp[0][i]):
                cut[i]=0
            else:
                for idx in range(1,i+1):
                    cut[i]=min(cut[i],cut[idx-1]+1) if dp[idx][i] else cut[i]
        
        # print(dp)
        # print(cut)
        
        return cut[-1]

2021/9/14

1035. 不相交的线

这题基本和1143题一样

class Solution:
    def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int:
        # dp[i][j]表示num1前i个和num2前j个 最多的连线
        # dp[i-1]==dp[j-1] dp[i][j]=dp[i-1][j-1]+1
        # dp[i-1]!=dp[j-1] dp[i][j]=max(dp[i-1][j],dp[i][j-1])
        m,n=len(nums1),len(nums2)
        dp=[0]*(n+1)

        for i in range(1,m+1):
            dp_new=[0]*(n+1)
            for j in range(1,n+1):
                if(nums2[j-1]==nums1[i-1]):
                    dp_new[j]=dp[j-1]+1
                else:
                    dp_new[j]=max(dp[j],dp_new[j-1])
            dp=dp_new
            
        return dp[-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值