• 【Leetcode】DP系列


    【Leetcode-5】

    一、题目:最长回文字串

      给你一个字符串 s,找到 s 中最长的回文子串。

    二、代码:

    def longestPalindrome(self, s: str) -> str:
            start, end = -1, -1
            max_len = -1
            if len(s) == 0:
                return ""
            n = len(s)
            arr = [[False]*n for _ in range(n)]
            for i in range(n, -1, -1):
                
                for j in range(i, n):
                    if s[i] == s[j] and (j-i==1 or arr[i+1][j-1]):
                       arr[i][j] = True
                    if arr[i][j] and (j-i+1>max_len):
                        start = i
                        end = j
                        max_len = j-i+1
            res_str = s[start: end+1]
            return res_str
            

    【Leetcode-53】

    一、题目:最大子序和

      给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    二、代码:

    def maxSubArray(self, nums: List[int]) -> int:
            max_value = float('-inf')
            value = 0
            for item in nums:
                value = max(item+value, item)
                max_value = max(max_value, value)
            return max_value

    【Leetcode-62】

    一、题目:不同路径

      一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

      机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

      问总共有多少条不同的路径?

    二、代码:

    def uniquePaths(self, m: int, n: int) -> int:
            dp = [[1]*n for _ in range(m)]
            for i in range(1, m):
                for j in range(1, n):
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
            return dp[m-1][n-1]

    【Leetcode-64】

    一、题目:最小路径和

      给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

      说明:每次只能向下或者向右移动一步。

    二、代码:

    def minPathSum(self, grid: List[List[int]]) -> int:
            m, n = len(grid), len(grid[0])
            dp = [[0]*n for _ in range(m)]
            dp[0][0] = grid[0][0]
            for i in range(1, m):
                dp[i][0] = dp[i-1][0] + grid[i][0]
            for j in range(1, n):
                dp[0][j] = dp[0][j-1] + grid[0][j]
            for i in range(1, m):
                for j in range(1, n):
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
            return dp[m-1][n-1]

    【Leetcode-70】

    一、题目:爬楼梯

      假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

      每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

      注意:给定 n 是一个正整数。

    二、代码:

    def climbStairs(self, n: int) -> int:
            f1 = f2 = f = 1
            for i in range(n-1):
                f = f1 + f2
                f2 = f1
                f1 = f
            return f

    【Leetcode-72】

    一、题目:编辑距离

      给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

    二、代码: 

    def minDistance(self, word1: str, word2: str) -> int:
            n1 = len(word1)
            n2 = len(word2)
            if n1 == 0 or n2 == 0:
                return max(n1, n2)
            dp = [[0]*(n2+1) for _ in range(n1+1)]
            for j in range(1, n2+1):
                dp[0][j] = dp[0][j-1] + 1
            for i in range(1, n1+1):
                dp[i][0] = dp[i-1][0] + 1
            
            for i in range(1, n1+1):
                for j in range(1, n2+1):
                    if word1[i-1] == word2[j-1]:
                        dp[i][j] = dp[i-1][j-1]
                    else:
                        dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])
            return dp[-1][-1]
            

    【Leetcode-96】

    一、题目:不同的二叉搜索树

      给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

    二、代码:

    def numTrees(self, n: int) -> int:
            f = [0]*(n+1)
            f[0] = 1
            f[1] = 1
            for i in range(2, n+1):
                for k in range(i):
                    f[i] += f[k]*f[i-k-1]
            return f[n]

    【Leetcode-96】

    一、题目:

      给定一个三角形 triangle ,找出自顶向下的最小路径和。

      每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

    二、代码:

     def minimumTotal(self, triangle: List[List[int]]) -> int:
            """
            2
            3 4
            6 5 7
            4 1 8 3
            0 0 0 0 0
            1.自底而上,min(dp[j] + triangle[i][j], dp[j + 1] + triangle[i][j]) 0<=j<=i
            2.执行n次,返回dp[0]
            """
            n = len(triangle)
            dp = [0] * (n+1)
            for i in reversed(range(n)):
                for j in range(i+1):
                    dp[j] = min(dp[j] + triangle[i][j], dp[j + 1] + triangle[i][j])
            return dp[0]

    【Leetcode-121】

    一、题目:买卖股票的最佳时期

      给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

      你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

      返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

    二、代码:

    def maxProfit(self, prices: List[int]) -> int:
            min_val = prices[0]
            max_val = float('-inf')
            for p in prices:
                max_val = max(max_val, p-min_val, 0)
                min_val = min(min_val, p)
            return max_val

    【Leetcode-122】

    一、题目:买卖股票的最佳时期2

      给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

      设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

      注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    二、代码:

    class Solution:
        def maxProfit(self, prices: List[int]) -> int:
            """
            0表示该天结束无股票,1表示该天结束欧股票
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
            dp[i][1] = max(dp[i-1][0]-prices[i], dp[i-1][1])
            """
            f = [0, -prices[0]]
            max_pro = 0
            for item in prices[1:]:
                f0 = max(f[0], f[1]+item)
                f1 = max(f[0]-item, f[1])
                f = [f0, f1]
            return max(f)

    【Leetcode-309】

    一、题目:最佳买卖股票时机含冷冻期

      给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

      设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

      你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
      卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

    二、代码:

    def maxProfit(self, prices: List[int]) -> int:
            f = [-prices[0], 0, 0]  # 第0天,有股票/无股票非冷冻/无股票冷冻期
            for i in range(1, len(prices)):
                f[0] = max(f[1]-prices[i], f[0])
                f[1] = max(f[1], f[2])
                f[2] = f[0] + prices[i]
            return max(f[1], f[2]

    【Leetcode-152】

    一、题目:乘积最大子数组

      给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。(数组中有负数)

    二、代码:

    def maxProduct(self, nums: List[int]) -> int:
            min_val = max_val = res = nums[0]
            for item in nums[1:]:
                min1, max1 = min_val, max_val
                max_val = max(min1*item, item, max1*item)
                min_val = min(min1*item, item, max1*item)
                res = max(res, max_val)
            return res
            

    【Leetcode-198】

    一、题目:打家劫舍

      你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

      给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

    二、代码:

    def rob(self, nums: List[int]) -> int:
            f0 = 0
            f1 = nums[0]
            for item in nums[1:]:
                f = max(f0+item, f1)
                f0 = f1
                f1 = f
            return f1

    【Leetcode-221】

    一、题目:最大正方形

      在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

    二、代码:

    def maximalSquare(self, matrix: List[List[str]]) -> int:
            m, n = len(matrix), len(matrix[0])
            dp = [[0]*n for _ in range(m)]
            max_len = 0
            for i in range(0, m):
                for j in range(0, n):
                    if matrix[i][j] == "1":
                        if i == 0 or j == 0:
                            dp[i][j] = 1
                        else:
                            dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])
                        max_len = max(max_len, dp[i][j])
            return max_len*max_len

    【Leetcode-279】

    一、题目:完全平方数

      给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

      给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

    二、代码:

    def numSquares(self, n: int) -> int:
            """
            dp[i] = dp[i-j*j]+1, j*j<=i
            """
            dp = [0]*(n+1)
            for i in range(1, n+1):
                dp[i] = i  # 最多由i个1组成
                j = 0
                while j*j <= i:
                    dp[i] = min(dp[i], dp[i-j*j]+1)
                    j += 1
            return dp[n]

    【Leetcode-322】

    一、题目:零钱兑换

      给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

      你可以认为每种硬币的数量是无限的。

    二、代码:

    def coinChange(self, coins: List[int], amount: int) -> int:
            n = len(coins)
            dp = [float('inf')] * (amount + 1)
            dp[0] = 0
            for i in range(n):
                for j in range(coins[i], amount+1):
                    dp[j] = min(dp[j-coins[i]]+1, dp[j])
            if dp[amount] < float('inf'):
                return dp[amount]
            else:
                return -1

    【Leetcode-518】

    一、题目:零钱兑换2

      给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

    二、代码:

    def change(self, amount: int, coins: List[int]) -> int:
            """
            硬币无限->完全背包问题,正序
            """
            dp = [0] * (amount+1)
            dp[0] = 1
            for c in coins:
                for j in range(c, amount+1):
                    dp[j] += dp[j-c]
            return dp[amount]

    【Leetcode-416】

    一、题目:分割等和子集

      给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

    二、代码:

    def canPartition(self, nums: List[int]) -> bool:
            if sum(nums) % 2 != 0:
                return False
            target = int(sum(nums) / 2)
            dp = [False] * (target+1)
            dp[0] = True
            for i in range(len(nums)):
                for j in reversed(range(nums[i], target+1)):
                    dp[j] = max(dp[j], dp[j-nums[i]])
            return dp[-1]

    【Leetcode-494】

    一、题目:目标和

      给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

      返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

    二、代码:

    def findTargetSumWays(self, nums: List[int], S: int) -> int:
            """
            转换成01背包问题:
            正数个数x,负数个数y,则x+y=n,x-y=s,x=(s+n)/2,即从数组中找出一些数,和为(s+n)/2
            """
            n = sum(nums)
            if (S+n) % 2 != 0:
                return 0
            if S > n:
                return 0
            target = (S+n) // 2
            dp = [0]*(target+1)
            dp[0] = 1  # 和为0的方法有1种
            for i in range(len(nums)):
                for j in reversed(range(nums[i], target+1)):
                    dp[j] = dp[j] + dp[j-nums[i]]
            return dp[-1]

    【Leetcode-560】

    一、题目:和为K的子数组

      给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

      数组的长度为 [1, 20,000]。

      数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

    二、代码:

    def subarraySum(self, nums: List[int], k: int) -> int:
            """
            前缀和
            [0,i]的和为total,以i为结尾的和为k的子数组个数为this_num,前缀和次数为pre_cnt,则this_num = pre_cnt[total - k]
            """
            from collections import defaultdict
            pre_cnt = defaultdict(int)  # 前缀和出现的次数
            pre_cnt[0] = 1  # 前缀和为0的有1种
            total = 0
            res = 0
            for item in nums:
                total += item  # 现在的前缀和
                this_num = pre_cnt[total - k]  # 满足差值为k的前缀和
                res += this_num
                pre_cnt[total] += 1
            return res

    【Leetcode-647】

    一、题目:回文字串

      给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

      具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

    二、代码:

    def countSubstrings(self, s: str) -> int:
            """
            计算[i, j]位置是否是回文子串,然后计算个数
            """
            if len(s) == 0:
                return 0
            n = len(s)
            num = 0
            arr = [[False]*n for _ in range(n)]
            for i in range(n-1, -1, -1):
                for j in range(i, n):
                    if s[i] == s[j] and (j-i<=1 or arr[i+1][j-1]):
                        arr[i][j] = True
                        num += 1
            return num

      

    【Leetcode-813】

    一、题目:最大平均值的分组

      我们将给定的数组 A 分成 K 个相邻的非空子数组 ,我们的分数由每个子数组内的平均值的总和构成。计算我们所能得到的最大分数是多少。

      注意我们必须使用 A 数组中的每一个数进行分组,并且分数不一定需要是整数

    二、代码:

    def largestSumOfAverages(self, A: List[int], K: int) -> float:"""
            dp[i][k]表示0~i(包括i)元素分成k份能获得的均值总和,则
            dp[i][k] = max(dp[j][k-1]+avg(j+1, i)), 0<=j<i
            dp[0][k] = A[0] ,表示0号元素拆分为K份的均值,为A[0]/1+0=A[0]
            dp[i][1] = avg(0, i),表示把0-i元素分成1份
            返回值为dp[n-1][K]
            """
            n = len(A)
            dp = [[0] * (K+1) for _ in range(n)]
            for k in range(1, K+1):
                dp[0][k] = A[0]
            for i in range(n):
                dp[i][1] = sum(A[:i+1])/(i+1)
            for i in range(1, n):
                for k in range(2, K+1):
                    for j in range(i):
                        dp[i][k] = max(dp[i][k], dp[j][k - 1] + sum(A[j + 1:i+1]) / (i - j))
            return dp[n-1][K]

    【Leetcode-1024】

    一、题目:视频拼接

      你将会获得一系列视频片段,这些片段来自于一项持续时长为 T 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。

      视频片段 clips[i] 都用区间进行表示:开始于 clips[i][0] 并于 clips[i][1] 结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7] 可以剪切成 [0, 1] + [1, 3] + [3, 7] 三部分。

      我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, T])。返回所需片段的最小数目,如果无法完成该任务,则返回 -1 。

    • 1 <= clips.length <= 100
    • 0 <= clips[i][0] <= clips[i][1] <= 100
    • 0 <= T <= 100

    二、代码:

    def videoStitching(self, clips: List[List[int]], T: int) -> int:
            """
            分为判断是否能到达T和计算需要最少个数
            判断是否能到达T:计算每个时间点能到达的点,若未到T走不动了(i最远只能到达i)说明不能到达T
            计算最少个数:贪心选择,每次选个能到达时间最远的,用完了就换一个到达时间最远的,个数最少,需要记录前面的时间片可到达的地方
            注意:T可能<clips所给范围
            """
            # 计算每个时间点能到达的时间点
            arrive = [0] * (T)
            for s, e in clips:
                if s < T:
                    arrive[s] = max(arrive[s], e)
            # 贪心选择
            res = 0
            now_arrive = 0  # 现在用的时间片可到达的地方,用于判断时间片是否用完
            max_arrive = 0 # 已有的时间片可到达的地方,用于计算换时间片可以到达的地方
            for time in range(T):
                max_arrive = max(max_arrive, arrive[time])
                if time == max_arrive:
                    return -1
                if now_arrive == time:  # 现在时间片用完,需要加时间片
                    res += 1
                    now_arrive = max_arrive
            return res 

     

    博文转载请注明出处。
  • 相关阅读:
    软链接和硬链接
    Hive的基本常识
    Hadoop
    Docker技术
    人生苦短,我用Python(6)
    人生苦短,我用Python(5)
    人生苦短,我用Python(4)
    openssl telnet openssh
    iptables
    http与httpd
  • 原文地址:https://www.cnblogs.com/EstherLjy/p/14608308.html
Copyright © 2020-2023  润新知