【科学刷题】动态规划

  1. 判断子序列

加到【编辑距离】中

文章目录

  • 解码方法 / 把数字翻译成字符串
  • 174. 地下城游戏
  • 403. 青蛙过河
  • 不同路径(考虑障碍物的DP)
  • 股票难题
  • 718. 最长重复子数组
  • 516. 最长回文子序列
  • 97. 交错字符串
  • 416. 分割等和子集
  • 312. 戳气球
  • 72. 编辑距离
  • 322. 零钱兑换
  • 10. 正则表达式匹配
  • 198. 打家劫舍

解码方法 / 把数字翻译成字符串

剑指 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. 地下城游戏

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. 青蛙过河

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

不同路径(考虑障碍物的DP)

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. 最长重复子数组

【科学刷题】动态规划_第1张图片

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. 最长回文子序列

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. 交错字符串

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. 分割等和子集

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. 戳气球

312. 戳气球

经典动态规划:戳气球问题

有空再研究

【科学刷题】动态规划_第2张图片

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. 编辑距离

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. 零钱兑换

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. 正则表达式匹配

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]

【科学刷题】动态规划_第3张图片

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. 打家劫舍

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)

你可能感兴趣的:(科学刷题,动态规划,算法)