Day 2 数组拓展:904. 水果成篮, 76. 最小覆盖子串, 54. 螺旋矩阵, 48. 旋转图像; 数组总结

904. 水果成篮

  • 思路
    • example
    • 两个篮子,返回你可以收集的水果的 最大 数目
    • 等价于“最长的含两个不同数字的连续子序列长度”
    • Two Pointer, Sliding Window, -->-->
      • left, right = 0, 0
      • use hash, i.e., dict to record the fruits
      • dict[num] = freq
      • len(dict) is always <= 2
      • [left, right] is feasible if len(dict) <= 2,左闭右闭,可行窗口
  • 注意最大最小的处理逻辑不同。

本题

遍历 right:
    处理right
    循环收缩left找到可行窗口
    处理可行窗口
  • 复杂度. 时间:O(n), 空间: O(1)
class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        n = len(fruits)
        left, right = 0, 0
        basket = collections.defaultdict(int)
        res = 0
        while right < n:
            # "add"/test current fruit into the basket
            basket[fruits[right]] += 1
            # then check the feasibility
            while len(basket) > 2: # more than 2 types of fruits, [left, right] is not feasible
                # move left
                basket[fruits[left]] -= 1
                if basket[fruits[left]] == 0:
                    del basket[fruits[left]] # need to del this otherwise we could not use len(basket)
                left += 1
            # [left, right] is feasible, update res
            res = max(res, right - left + 1)
            # move right
            right += 1
        return res
  • try this
import collections
dict1 = collections.defaultdict(int)
print(dict1)
print(dict1[2])
print(len(dict1))
print(dict1[3])
print(len(dict1))
print(dict1)

76. 最小覆盖子串

  • 思路
    • example
    • 等价于“最短的连续子串(包含t)"
    • sliding window, -->-->
      • left, right = 0, 0
      • two hashes, cnt_s, cnt_t, "cnt[ch] = freq"
      • feasible window [left, right]
        • valid == len(t) (valid always <= len(t))
        • it's possible that cnt_s[ch] > cnt_t[ch] for some ch in s[left: right+1]
      • once find a feasible window, keep moving left to update the result (needs smallest substring)
      • Key: for a feasible window, cnt_s[s[right]] is exactly equal to cnt_t[s[right]]
hash: cnt_s (变化),cnt_t (不变)
valid记录当前窗口含有t元素的个数。(valid 最大值n)
遍历right:
    处理right (更新cnt_s, valid)
    如果窗口可行(size=n), 循环收缩left找到满足要求的最小窗口 (更新cnt_s, valid, 答案)
  • 复杂度. 时间:O(n), 空间: O(1)
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m, n = len(s), len(t)
        if m < n:
            return ''
        # fix cnt_t
        cnt_t = collections.defaultdict(int) 
        for ch in t:
            cnt_t[ch] += 1
        #
        cnt_s = collections.defaultdict(int) 
        left, right = 0, 0
        valid = 0
        start, end = 0, -1
        min_len = float('inf') # smallest length of feasible window
        while right < m:
            ch = s[right]
            cnt_s[ch] += 1
            if cnt_s[ch] <= cnt_t[ch]: # add one valid char
                valid += 1
            while valid == n: # [left, right] is a feasible, keep moving left to find the smallest window
                cur_len = right - left + 1
                if cur_len < min_len:
                    min_len = cur_len
                    start, end = left, right # record
                cnt_s[s[left]] -= 1
                if cnt_s[s[left]] < cnt_t[s[left]]: # cnt_t[s[left]] is at least 0 (defaultdict)
                    valid -= 1
                left += 1
            # move right
            right += 1
        return s[start:end+1]
  • version 2: 每一步都把left指针移动到“最佳“位置。
    • Key: for a feasible window, cnt_s[s[left]] is exactly equal to cnt_t[s[left]]
hash: cnt_s (变化),cnt_t (不变)
valid记录当前窗口含有t元素的个数。(valid 最大值n)
遍历right:
    处理right (更新cnt_s, valid)
    循环收缩left直到左边界处在最佳位置 (cnt_s[s[left]] == cnt_t[s[left]]),结束后得到“预可行”窗口(valid < n)
    如果窗口可行,更新最小值
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m, n = len(s), len(t)
        if m < n:
            return ''
        # fix cnt_t
        cnt_t = collections.defaultdict(int) 
        for ch in t:
            cnt_t[ch] += 1
        #
        cnt_s = collections.defaultdict(int) 
        left, right = 0, 0
        valid = 0
        start, end = 0, -1
        min_len = float('inf') # smallest length of feasible window
        while right < m:
            ch = s[right]
            cnt_s[ch] += 1
            if cnt_s[ch] <= cnt_t[ch]: # add one valid char
                valid += 1
            # keep moving left to remove the unnecessary char, until cnt_s[s[left]] == cnt_t[s[left]] 
            while left <= right and cnt_s[s[left]] > cnt_t[s[left]]:
                cnt_s[s[left]] -= 1
                left += 1
            # check feasibility
            if valid == n: # now [left, right] will be the shortest feasible substring 
                cur_len = right - left + 1
                if cur_len < min_len:
                    min_len = cur_len 
                    start, end = left, right 
            # move right
            right += 1
        return s[start:end+1]

54. 螺旋矩阵

  • 思路
    • example
    • size of matrix: m \times n
    • slightly change the update style inside the loop
      • 贪心策略:右,下,左,上。每个方向都尽可能搜索,这样可以方便处理单行单列的情况。
    • deal with the boundary case (eg., one row, one column)
    • should work for m==n case
  • 复杂度. 时间:O(n^2), 空间: O(1)
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m, n = len(matrix), len(matrix[0])
        top, bottom = 0, m-1
        left, right = 0, n-1
        res = []
        while top <= bottom and left <= right:
            #-->
            for j in range(left, right+1):
                res.append(matrix[top][j])
            # downward (right column)
            for i in range(top+1, bottom+1):
                res.append(matrix[i][right])
            #<--
            if top < bottom and left < right: # extra row or column updates
                for j in range(right-1, left-1, -1):
                    res.append(matrix[bottom][j])
                for i in range(bottom-1, top, -1):
                    res.append(matrix[i][left])
            # update indices
            top += 1
            bottom -= 1
            left += 1
            right -= 1
        return res

48. 旋转图像

  • 思路
    • example
    • size of matrix: n \times n

先按主对角线翻转
再每一行进行翻转



class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        def reverse(nums):
            n = len(nums)
            left, right = 0, n-1
            while left < right:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
                right -= 1
        # step 1
        n = len(matrix)
        for i in range(n):
            for j in range(i+1, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        # step 2
        for i in range(n):
            reverse(matrix[i])

数组总结

  1. 二分搜索
  • 左闭右闭
  • 左闭右开
  1. 双指针
  • 快慢,前后, 。。。
  • 滑动窗口
    • 同向移动 -->-->
    • left, right 初始化
    • 外层循环:right
    • 内层:left
    • 判断当前窗口可行性,移动left
      • 计数
      • hash技术
    • 滑动窗口:本质是满足了单调性,即左右指针只会往一个方向走且不会回头。收缩的本质即去掉不再需要的元素。也就是做题我们可以先固定移动右指针,判断条件是否可以收缩左指针算范围。大家可以好好理解一下。From 芒果冰
    • <--*-->
    • -->*<--
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容