【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76

32. 最长有效括号
【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76_第1张图片
视频题解

class Solution(object):
    def longestValidParentheses(self, s):
        dp = [0 for x in range(len(s))]
        max_to_now = 0
        for i in range(1,len(s)):
            if s[i] == ')':
                # case 1: ()()
                if s[i-1] == '(':
                    #  在字符创末尾遇到左括号"("是构不成有效的括号的,所以就等于最近的括号加上2 
                    dp[i] = dp[i-2] + 2
                # case 2: (()) 
                # i-dp[i-1]-1 is the index of last "(" not paired until this ")"
                elif i-dp[i-1]-1 >= 0 and s[i-dp[i-1]-1] == '(':
                    if dp[i-1] > 0: # content within current matching pair is valid 
                    # add nearest parentheses pairs + 2 + parentheses before last "("
                        dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2]
                    else:
                    # otherwise is 0
                        dp[i] = 0
                max_to_now = max(max_to_now, dp[i])
        return max_to_now

64. 最小路径和

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

91. 解码方法

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s:
            return 0 
        dp = [0 for _ in range(len(s) + 1)]
        dp[0] = 1
        dp[1] = 0 if s[0] == '0' else 1
        for i in range(2, len(s) + 1):
            if 0 < int(s[i-1:i]) <= 9:
                dp[i] += dp[i-1]
            if 10 <= int(s[i-2:i]) <=26:
                dp[i] += dp[i-2]
        return dp[-1]

参考题解
【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76_第2张图片
221. 最大正方形

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0 
        m, n = map(len, (matrix, matrix[0]))
        dp = [[0] * (n+1) for _ in range(m+1)]
        max_side = 0 
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '1':
                    dp[i+1][j+1] = min(dp[i][j+1], dp[i+1][j], dp[i][j]) + 1
                    max_side = max(max_side, dp[i+1][j+1])
        return max_side * max_side

详细图文题解链接
【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76_第3张图片
363. 矩形区域不超过 K 的最大数值和

class Solution(object):
    def maxSumSubmatrix(self, matrix, k):
        # 计算前缀和的同时在出现过的前缀和中查找满足条件的值
        # 二维前缀和
        m, n = map(len, (matrix, matrix[0]))
        pre=[[0]*(n+1)for _ in range(m+1)]
        for i in range(m):
            for j in range(n):
                pre[i+1][j+1]=pre[i][j+1]+pre[i+1][j]+matrix[i][j]-pre[i][j]
        def sumRange(x1,y1,x2,y2):
            return pre[x2][y2]-pre[x1][y2]-pre[x2][y1]+pre[x1][y1]

        res=float('-inf')
        for i in range(n):
            for j in range(i+1,n+1):
                d=[0]
                # 在一定宽度范围,累加每一行
                # 当前累加和s来说,二分查找是否已经有过比s-k小的和
                for l in range(1,m+1):
                    s=sumRange(0,i,l,j)
                    idx=bisect.bisect_left(d,s-k)
                    if idx<len(d):
                        res=max(res, s-d[idx])
                    bisect.insort(d,s)
        return res

403. 青蛙过河

class Solution:
    def canCross(self, stones: List[int]) -> bool:
        # DP dynamic programming, DP table pi = dict()
        # pi[i] = {distance of last jump when frog at stone pos i}
        # Let n be the input size (size of stones)
        # T(n) = O(n^2)
        # S(n) = O(n^2)


        # init data struct
        # Time: O(n)
        pi = dict()
        for stone in stones:
            pi[stone] = set()
        
        # init state
        # Time: O(1)
        pi[0].add(0)
        
        # fill DP table pi
        # Time: O(n) * O(n) * O(1) = O(n^2)
        for stone in stones:
            for k in pi[stone]:
                for jump in [k-1, k, k+1]:
                    if jump > 0 and stone + jump in pi:
                        pi[stone+jump].add(jump)
        
        # return bool result
        return len(pi[stones[-1]]) > 1

来源
简化逻辑如下:

class Solution:
    def canCross(self, stones: List[int]) -> bool:
        pi = {
     stone:set() for stone in stones}
        pi[0].add(0)
        for stone in stones:
            for k in pi[stone]:
                for jump in [k-1, k, k+1]:
                    if jump > 0 and stone + jump in pi:
                        pi[stone+jump].add(jump)

        return len(pi[stones[-1]]) > 0

410. 分割数组的最大值

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:          
        #  指定二分查找范围    
        left, right = max(nums), sum(nums)    

        #定义 测试中点是大还是小的 测试函数
        def test_mid(mid):
            #初始化
            num = 1 #num表示使用该mid我们会得到几个数组
            cur_sum = 0 #s表示当前数组的和

            for i in nums:
                if cur_sum+i > mid: #如果当前数组已经超过mid,要停止这个数组
                    cur_sum = i #这个数变为下一个数组的开头
                    num += 1 #会得到的数组数量+1
                else:
                    cur_sum += i

            return num > m #数组总数是否>m, 大于的话说明mid太小,二分查找取右边
            #这里有一个注意点,如果num已经等于m了, 但此时如果left不等于right,范围还是会继续收敛的,
            #且取的是左半边,目的是让我们能最终找到一个确切的值,这个值恰好就是取得了最大值的那个数组的和
            #(因为小于这个和的话,就不能通过num=m的测试;而大于这个m的话,即使通过了num=m的测试,
            #范围也会继续向左边收敛,直到我们找到的就是这个和)。
        
        #进行二分查找
        while left < right: #当left == right的时候就终止查找,返回任意一个
            mid = (left + right) // 2
            is_right = test_mid(mid)
            if is_right:
                left = mid+1
            else:
                right = mid #num <= m的情况

        return left

552. 学生出勤记录 II
【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76_第4张图片
参考
621. 任务调度器

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        tasks_count = list(collections.Counter(tasks).values())
        max_count = max(tasks_count)
        max_count_tasks = tasks_count.count(max_count)
        return max(len(tasks), (max_count - 1) * (n + 1) + max_count_tasks)

参考
647. 回文子串

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        count = 0
        dp = [True]   
        for i in reversed(range(n)):  # reversed 返回一个逆转的迭代器
            next_dp = [True]
            next_dp.append(True)
            count += 1
            for j in range(i + 1, n):
                # substring length: j - i + 1 (>= 2)
                if s[i] == s[j] and dp[j - i - 1]:
                    next_dp.append(True)
                    count += 1
                else:
                    next_dp.append(False)
            dp = next_dp
                
        return count

【leetcode】动态规划32、64、91、221、363、403、410、552、621、647、76_第5张图片
76. 最小覆盖子串

from collections import Counter

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        '''
        Keep t_counter of char counts in t
        
        We make a sliding window across s, tracking the char counts in s_counter
        We keep track of matches, the number of chars with matching counts in s_counter and t_counter
        Increment or decrement matches based on how the sliding window changes
        When matches == len(t_counter.keys()), we have a valid window. Update the answer accordingly
        
        How we slide the window:
        Extend when matches < chars, because we can only get a valid window by adding more.
        Contract when matches == chars, because we could possibly do better than the current window.
        
        How we update matches:
        This only applies if t_counter[x] > 0.
        If s_counter[x] is increased to match t_counter[x], matches += 1
        If s_counter[x] is increased to be more than t_counter[x], do nothing
        If s_counter[x] is decreased to be t_counter[x] - 1, matches -= 1
        If s_counter[x] is decreased to be less than t_counter[x] - 1, do nothing
        
        Analysis:
        O(s + t) time: O(t) to build t_counter, then O(s) to move our sliding window across s. Each index is only visited twice.
        O(s + t) space: O(t) space for t_counter and O(s) space for s_counter
        '''
        
        if not s or not t or len(s) < len(t):
            return ''
        
        t_counter = Counter(t)
        chars = len(t_counter.keys())
        
        s_counter = Counter()
        matches = 0
        
        answer = ''
        
        i = 0
        j = -1 # make j = -1 to start, so we can move it forward and put s[0] in s_counter in the extend phase 
        
        while i < len(s):
            
            # extend
            if matches < chars:
                
                # since we don't have enough matches and j is at the end of the string, we have no way to increase matches
                if j == len(s) - 1:
                    return answer
                
                j += 1
                s_counter[s[j]] += 1
                if t_counter[s[j]] > 0 and s_counter[s[j]] == t_counter[s[j]]:
                    matches += 1

            # contract
            else:
                s_counter[s[i]] -= 1
                if t_counter[s[i]] > 0 and s_counter[s[i]] == t_counter[s[i]] - 1:
                    matches -= 1
                i += 1
                
            # update answer
            if matches == chars:
                if not answer:
                    answer = s[i:j+1]
                elif (j - i + 1) < len(answer):
                    answer = s[i:j+1]
        
        return answer

参考

你可能感兴趣的:(算法和数据结构刷题,leetcode,算法,动态规划)