滑动窗口模板 解题关键在确定何时收缩窗口

为什么要「左闭右开」区间–方便处理边界条件 滑动窗口也是数组指针的技巧之一, 是快慢指针技巧分支里面的一个。子串问题就要想到滑动窗口

理论上你可以设计两端都开或者两端都闭的区间,但设计为左闭右开区间是最方便处理的

因为这样初始化 left = right = 0 时区间 [0, 0) 中没有元素,但只要让 right 向右移动(扩大)一位,区间 [0, 1) 就包含一个元素 0 了。

如果你设置为两端都开的区间,那么让 right 向右移动一位后开区间 (0, 1) 仍然没有元素;如果你设置为两端都闭的区间,那么初始区间 [0, 0] 就包含了一个元素。这两种情况都会给边界处理带来不必要的麻烦。左闭右开区间的特性: **这样子,长度= right - left ** 不需要进行 + 1

第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,就好像一条毛毛虫,一伸一缩,不断向右滑动,这就是「滑动窗口」这个名字的来历。 第二步:不断扩大窗口,直到窗口符合要求。 第三步,right不动,增加left,缩小窗口,直到窗口中的字符串不符合要求,同时,每次增加left的时候都需要更新一轮结果

  • python中的defaultdict, 这里使用 int 作为默认值类型,意味着当你尝试访问字典中不存在的键时,会自动创建一个初始值为0的整数。 使用defaultdict可以使代码更简洁,避免了检查键是否存在的额外步骤。

leetcode 76 最小覆盖子串,多默写几遍

如果一个字符进入窗口,应该增加 window 计数器;如果一个字符将移出窗口的时候,应该减少 window 计数器;当 valid 满足 need 时应该收缩窗口;应该在收缩窗口的时候更新最终结果。

class Solution:

    def minWindow(self, s: str, t: str) -> str:
        from collections import defaultdict

        need, window = defaultdict(int), defaultdict(int)
        for c in t:
            need[c] += 1

        left, right = 0, 0 #都初始化为0,区间是左闭右开的,一开始me
        valid = 0
        # 记录最小覆盖子串的起始索引及长度,没有找到最小覆盖的时候,start,和length是不变的
        start, length = 0, float('inf')
        while right < len(s):  #拓展的条件
            # c 是将移入窗口的字符
            c = s[right] #放进去之后,right立马右移
            # 扩大窗口
            right += 1
            # 进行窗口内数据的一系列更新
            if c in need:  #这里上下是对称的
                window[c] += 1
                if window[c] == need[c]:
                    valid += 1

            # 判断左侧窗口是否要收缩,是在收缩窗口的阶段,进行最小子串的更新
            while valid == len(need):
                # 在这里更新最小覆盖子串
                if right - left < length:
                    start = left
                    length = right - left

                # d 是将移出窗口的字符
                d = s[left]
                # 缩小窗口
                left += 1 #放进去之后,left立马右移动
                # 进行窗口内数据的一系列更新
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1

        # 返回最小覆盖子串
        return "" if length == float('inf') else s[start:start + length]
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        from collections import defaultdict
#初始化字典
        need,window = defaultdict(int), defaultdict(int) #window 窗口用于记录当前target来的个数
        for c in t:
            need[c] += 1
#初始化操作所需参数 
        left, right = 0, 0 
        valid = 0
        start, length = 0, float('inf')
#扩大区间
        while right < len(s):
            c = s[right]
            right += 1
            if c in need:#用来判断c是否是字典need中的一个值,如果是的话再进行操作
                window[c] += 1
                if window[c] == need[c]:
                    valid += 1
#缩小区间,
            while valid == len(need):
                if right - left < length:
                    start = left 
                    length = right - start  #满足长度小于的时候才更新长度,不是每一次都更新

                d = s[left]
                left += 1
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        return '' if length ==float('inf') else s[start: start + length]#切片操作也是左闭右开进行的。
        				

python中for c in need 判断c 是否是need字典的一个键, len(need) 返回的是need字典中键值对的数量。

leetcode 567 字符串的排列

题目条件没读清楚,但是也能通过93个用例, s2 要只包含s1 的排列,且不包含其他元素。

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        from collections import defaultdict 
        need, window = defaultdict(int), defaultdict(int)
        for c in s1:
            need[c] += 1
        left, right = 0, 0 
        start = 0
        valid = 0 
        length = float('inf')

        while right < len(s2):
            c = s2[right]
            right += 1
            if c in need:
                window[c] += 1
                if window[c] == need[c]:
                    valid += 1
            while right - left >= len(s1):#
                if valid == len(need):
                    return True
                d = s2[left]
                left += 1
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        return False

 #重要的是收缩窗口的条件。

窗口收缩的条件通常是当前窗口满足或超出特定约束,比如长度达到目标子串的长度,或者包含了所有需要的字符。

leetcode438 找到字符串中的所有字母异位词

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        from collections import defaultdict
        window, need = defaultdict(int), defaultdict(int)
        res = []
        for c in p:
            need[c] += 1
        left, right = 0, 0 
        valid = 0
        while right < len(s):
            c = s[right]
            right += 1
            if c in need:
                window[c] += 1
                if window[c] == need[c]:
                    valid += 1
            while (right - left) >= len(p):
                if valid == len(need):
                    res.append(left)
                d = s[left]
                left += 1
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        return res
##模板的魅力,自己的都不相信自己一遍写过了

这一题中,窗口满足的收缩条件就是长度达到目标字串p的长度,就开始收缩

收缩的目的就是优化可行解

leetcode3 无重复字符的最长子串

python中dictionary.get(key, default=None)

  • 获取键的值:如果字典中存在键 key,则 get 方法返回该键对应的值。
  • 返回默认值:如果字典中不存在键 key,则返回 default 指定的值。如果不指定 default,则默认返回 None。

收缩窗口的时机:当右移到不符合条件的时候收缩窗口,比如window[c] > 1,这不符合“无重复字符”的条件,所以开始收缩窗口 总的来说,收缩的时机都是满足题目要求的临界条件时候再收缩 窗口都是用字典存储 python中是 {}

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        window = {}
        left, right = 0, 0 
        res = 0 
        while right < len(s):
            c = s[right] 
            right += 1
            window[c] = window.get(c,0) + 1
            while window[c] > 1:
                d= s[left]
                left += 1
                window[d] -= 1 #每次左移都能确保这里面没有重复元素
            res = max(res, right - left) #在每次窗口内无重复字符时,更新结果 res,存储当前无重复字符子串的最大长度。
        return res

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值