《算法通关之路》-chapter10滑动窗口

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

滑动窗口最大值

力扣第239题
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。

'''
方法一:暴力法(超时)
时间复杂度:O(nk)
空间复杂度:O(1)
'''
class Solution:
    def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
        left, right = 0, k
        res = []
        while right <= len(nums):
            res.append(max(nums[left: right]))
            left, right = left + 1, right + 1
        return res

nums, k = [1,3,-1,-3,5,3,6,7], 3
solu = Solution()
solu.maxSlidingWindow(nums, k)
'''
方法二:单调队列
时间复杂度:O(n)
空间复杂度:O(k)
'''
class Solution:
    def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
        window = list()
        res = list()
        for i, n in enumerate(nums):
            # 从尾部开始移除比新加入元素小的元素
            while window and nums[window[-1]] < n:
                window.pop()
            # 将新加入的元素添加到window
            window.append(i)
            # 如果窗口外的元素仍然在window中,则将其移除
            if window[0] == i-k:
                window.pop(0)
            # 将当前元素即当前最大元素对应数字放入结果数组
            if i >= k - 1:
                res.append(nums[window[0]])
        return res

nums, k = [1,3,-1,-3,5,3,6,7], 3
solu = Solution()
solu.maxSlidingWindow(nums, k)

最小覆盖子串

力扣第76题
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

'''
方法二:滑动窗口
'''
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        max_window = s + 'a' # 最大窗口
        t_dict = dict() # 哈希表记录t中的字符

        # 统计t中的字符
        for ch in t:
            if not t_dict.get(ch):
                t_dict[ch] = 0
            t_dict[ch] += 1
        
        # 判断当前窗口中是否包含全部t
        def all_elem_zero() -> bool:
            for key, _ in t_dict.items():
                if t_dict[key] > 0:
                    return False
            return True
        
        left, right = 0, 0
        while right < len(s):
            if s[right] in t_dict: 
                t_dict[s[right]] -= 1
                while all_elem_zero(): # 当前窗口包含全部t
                    while s[left] not in t_dict: # 缩小窗口时如果遇到不是t中的字符直接跳过
                        left += 1
                    if right -left + 1 < len(max_window): # 更新最小窗口值
                        max_window = s[left:right+1]
                    if s[left] in t_dict: # 之前减掉的值加回来
                        t_dict[s[left]] += 1
                    left += 1
            right += 1

        return "" if max_window == s + 'a' else max_window

s, t = "ADOBECODEBANC", "ABC"
solu = Solution()
solu.minWindow(s, t)

替换后的最长重复字符

力扣第424题
给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回包含相同字母的最长子字符串的长度。

'''
方法一:滑动窗口
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        max_len = 0
        ch_cnt = dict() # 记录字符出现频率
        left, right = 0, 0
        while right < len(s):
            if s[right] not in ch_cnt:
                ch_cnt[s[right]] = 0
            ch_cnt[s[right]] += 1
            if max(ch_cnt.values()) + k < right - left + 1: # 频率最高的字符加上k比当前窗口小,则收缩左窗口
                ch_cnt[s[left]] -= 1
                left += 1
            max_len = max(max_len, right-left+1) # 现在频率最高的字符加上k的长度与当前窗口相等或更大
            right += 1
        return max_len

s, k = "ABAB", 2
solu = Solution()
solu.characterReplacement(s, k)
'''
方法二:滑动窗口(避免每次求max)
时间复杂度:O(n)
空间复杂度:O(1)
'''
import collections
class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        left = right = 0
        max_char_n = 0
        counts = collections.Counter()
        for right in range(0, len(s)):
            counts[s[right]] += 1
            max_char_n = max(max_char_n, counts[s[right]])

            if right - left + 1 > k + max_char_n:
                counts[s[left]] -= 1
                left += 1
        return right - left + 1

s, k = "ABAB", 2
solu = Solution()
solu.characterReplacement(s, k)
'''
方法三:滑动窗口(更加工整的版本)
时间复杂度:O(n)
空间复杂度:O(1)
'''
import collections
class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        max_char_n = res = 0 
        counts = collections.Counter()
        for i in range(len(s)):
            counts[s[i]] += 1
            max_char_n = max(max_char_n, counts[s[i]])

            if res - max_char_n < k:
                res += 1
            else:
                counts[s[i-res]] -= 1
        return res

s, k = "ABAB", 2
solu = Solution()
solu.characterReplacement(s, k)

字符串的排列

力扣第567题
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。

'''
方法一:滑动窗口
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        ch_cnt = dict() # 统计字符频次的字典
        for ch in s1:
            if ch not in ch_cnt:
                ch_cnt[ch] = 0
            ch_cnt[ch] += 1
        
        # 处理第一个窗口
        for ch in s2[0: len(s1)]:
            if ch in ch_cnt:
                ch_cnt[ch] -= 1
        if max(ch_cnt.values()) == 0:
                return True
        
        # 窗口向右移动
        left, right = 0, len(s1)
        while right < len(s2):
            # 迁移至下一个窗口
            if s2[left] in ch_cnt:
                ch_cnt[s2[left]] += 1
            if s2[right] in ch_cnt:
                ch_cnt[s2[right]] -= 1  
            if max(ch_cnt.values()) == 0:
                return True
            left += 1
            right += 1
        return False

s1, s2 = "ab", "eidbaooo"
solu = Solution()
solu.checkInclusion(s1, s2)

笔记本-Github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiawen9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值