Leetcode--遞歸、動態規劃(python)

至少有K個重複字符的最長子串

找到給定字符串(由小寫字符組成)中的最長子串 T , 要求 T 中的每一字符出現次數都不少於 k 。輸出 T 的長度。

示例 1:

輸入:
s = “aaabb”, k = 3

輸出:
3

最長子串爲 “aaa” ,其中 ‘a’ 重複了 3 次。
示例 2:

輸入:
s = “ababbc”, k = 2

輸出:
5

最長子串爲 “ababb” ,其中 ‘a’ 重複了 2 次, ‘b’ 重複了 3 次。

解法
看題目有點像用動態規劃的方法來做,但是並沒有好方法,而是用遞歸。
首先判斷字符串的長度,若小於k,直接返回0。然後對字符串中的每一個字符依次判斷其出現次數,若次數小於k,則在不包括該字符的子串中繼續遞歸判斷,最後返回各個符合條件子串長度的最大值即可。
代碼非常的Python化

class Solution(object):
    def longestSubstring(self, s, k):
        """
        :type s: str
        :type k: int
        :rtype: int
        """
        if len(s) < k:
            return 0
        
        for x in set(s):
            if s.count(x) < k:
                return max(self.longestSubstring(sub, k) for sub in s.split(x))
            
        return len(s)

二叉樹中的最大路徑和

給定一個非空二叉樹,返回其最大路徑和。

本題中,路徑被定義爲一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。

示例 1:

輸入: [1,2,3]

       1
      / \
     2   3

輸出: 6

示例 2:

輸入: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

輸出: 42

解法
首先理解題意:最大路徑和是指節點和最大的一條路徑的和。想到用遞歸,那麼轉移方程是什麼呢?
對於某個節點來說,有兩種情況,要麼被算入路徑中,要麼沒有,而且只能通過它是root的情況來判斷,即某節點作爲root算入路徑中。
如果該節點是最大路徑和的root,那麼就是這個節點的值+左右子樹的最大路徑和(若爲負數,就設爲0,即不算該子樹)。
注意遞歸的返回值應該是左右子樹較大的那個子樹加上當前節點的值,因爲往父節點回溯的話,最大路徑就不能同時包含左右兩個子樹。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def __init__(self):
        self.res = float("-inf")
        
    def maxPathSum(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        
        self.getmax(root)
        
        return self.res
    
    def getmax(self, root):
        if not root:
            return 0
        left = max(0, self.getmax(root.left))
        right = max(0, self.getmax(root.right))
        self.res = max(self.res, left + right + root.val)
        
        return max(left, right) + root.val
        
        

最長連續序列

給定一個未排序的整數數組,找出最長連續序列的長度。

要求算法的時間複雜度爲 O(n)。

示例:

輸入: [100, 4, 200, 1, 3, 2]
輸出: 4
解釋: 最長連續序列是 [1, 2, 3, 4]。它的長度爲 4。

解法
因爲時間複雜度是O(n)的,所以一般排序都不可以,只能用哈希的辦法。因爲是最長序列,爲了減少時間,方便哈希,先用set去除重複。
其實有點像動態規劃,只是用字典存儲每個可能的序列的最長長度。key是區間端點(某個值),val是這段區間的長度,
線性掃描輸入數組,如果當前元素不在哈希表裏,就看一下它左右兩側區間的長度left, right,計算出它自身的區間長度length = 1 + left + right。
注意計算完之後更新新的左右端點的長度爲,record[num - left] = length = record[num + right], 區間內部不需要更新,因爲不會再出現內部的值。

class Solution(object):
    def longestConsecutive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        nums = set(nums)
        
        res = 0
        dic = {}
        for idx, x in enumerate(nums):
            left = dic.get(x-1, 0)
            right = dic.get(x+1, 0)
            dic[x] = 1 + left + right
            res = max(res, dic[x])
            
            for i in [x - left, x + right]:
                dic[i] = dic[x]
            
        return res

打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [1,2,3,1]
輸出: 4
解釋: 偷竊 1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3)。
偷竊到的最高金額 = 1 + 3 = 4 。
示例 2:

輸入: [2,7,9,3,1]
輸出: 12
解釋: 偷竊 1 號房屋 (金額 = 2), 偷竊 3 號房屋 (金額 = 9),接着偷竊 5 號房屋 (金額 = 1)。
偷竊到的最高金額 = 2 + 9 + 1 = 12 。

解法
典型動態規劃問題:設dp[i]是到第i家能偷竊到的最大金額,而第i家有兩種情況:

  • 偷竊:dp[i-2]+nums[i]
  • 不偷竊:dp[i-1]

注意邊界條件

class Solution(object):
    def rob(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        if len(nums) == 1:
            return nums[0]
        if len(nums) == 2:
            return max(nums[0], nums[1])
        
        dp = [0] * len(nums)
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])
        
        for i in range(2, len(nums)):
            dp[i] = max(dp[i-2] + nums[i], dp[i-1])
            
        return dp[len(nums) - 1]
        

完全平方數

給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, …)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。

示例 1:

輸入: n = 12
輸出: 3
解釋: 12 = 4 + 4 + 4.
示例 2:

輸入: n = 13
輸出: 2
解釋: 13 = 4 + 9.

解法
參考多種解法

利用隊列和廣度優先遍歷的方法,隊列以[(當前節點的值,步數)]的方式存,利用廣度優先遍歷最先到終點的路線一定是最短路徑的特點,來找到最短步數。

class Solution(object):
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        
        q = [(n, 0)]
        visit = [False for i in range(n+1)]
        visit[n] = True
        
        while q:
            x, step = q.pop(0)
            
            i = 1
            rest = x - i**2
            while rest >= 0:
                if rest == 0:
                    return step + 1
                else:
                    if not visit[rest]:
                        visit[rest] = True
                        q.append((rest, step+1))
                        
                i += 1
                rest = x - i**2

最長上升子序列

給定一個無序的整數數組,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間複雜度應該爲 O(n2) 。
進階: 你能將算法的時間複雜度降低到 O(n log n) 嗎?

解法
第一種方法:動態規劃。
狀態的定義:以 num[i] 結尾的最長上升子序列的長度。
狀態轉移方程:之前的數中比 num[i] 小的最長上升子序列的長度 + 1。
第二種方法:貪心選擇 + 二分查找算法
參考https://blog.csdn.net/lw_power/article/details/80758674

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        
        res = [1 for i in range(len(nums))]
        
        for i in range(1, len(nums)):
            for j in range(0, i):
                if nums[j] < nums[i]:
                    res[i] = max(res[i], res[j] + 1)
                    
        return max(res)
class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        a=[]
        if len(nums)==0:
            return 0
        a.append(nums[0])
        for i in range(1,len(nums)):
            if nums[i]>a[-1]:
                a.append(nums[i])
            else:
                if nums[i]<a[0]:
                    a[0]=nums[i]
                else:
                    position=self.binarySearch(a,nums[i],0,len(a)-1)
                    a[position]=nums[i]
        return len(a)
            
    def binarySearch(self,a,number,left,right):
        if left==right:
            return left
        while left<right:
            mid=(left+right)/2
            if mid==left or mid==right:
                if number>a[left]:
                    return right
                else:
                    return left
            if number<a[mid]:
                return self.binarySearch(a,number,left,mid)
            else:
                return self.binarySearch(a,number,mid,right)

零錢兌換

給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。

示例 1:

輸入: coins = [1, 2, 5], amount = 11
輸出: 3
解釋: 11 = 5 + 5 + 1
示例 2:

輸入: coins = [2], amount = 3
輸出: -1
說明:
你可以認爲每種硬幣的數量是無限的。

解法
動態規劃:
狀態:金額 0 至金額 amount,dp 數組記錄當前金額的最少硬幣數。
選擇:當前金額下是否添加該硬幣(遍歷所有種類硬幣情況),取決於所需硬幣數的多少,若不添加則維持原硬幣數;若添加則更新爲 dp[當前金額-該硬幣]+1。

class Solution(object):
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        
        dp = [float("inf")] * (amount + 1)
        dp[0] = 0
        
        for x in range(amount+1):
            for y in coins:
                if y <= x:
                    dp[x] = min(dp[x], dp[x-y] + 1)
                    
        if dp[amount] == float("inf"):
            return -1
        else:
            return dp[amount]
        
發佈了150 篇原創文章 · 獲贊 24 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章