【算法训练】滑动窗口 leetcode python解法

一、最小覆盖子串 76

https://leetcode-cn.com/problems/minimum-window-substring/submissions/

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

1、分析

浅浅分析一下,我们需要找到s字符串中的一个子串,该子串能够包含t中的字符,而且字符的数量要和t中相应字符的数量一样。
我们先用字典统计出t中所有字符以及对应的数量,然后采用滑动窗口去记录访问到的s子串的相应字符数量。用valid标记t中字符出现次数满足条件的情况,若valid与t中字符数相等了,说明t中每个字符都在当前子串中出现了,而且这些字符的数量都是大于等于他们在t中的数量的。
此时就应该进行窗口的缩小了,在窗口缩小的过程中,不断更新子串的起始索引和长度,方便结果的返回。

具体可以看代码和注释。

2、代码

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need = Counter(t) #统计t中所有字符的数量
        window = collections.defaultdict(int)  #初始化滑动窗口
        valid = 0 #记录满足条件的字符数
        start,min_len = 0,float('inf') #记录最小子串的起始索引和长度
        left,right = 0,0 #滑动窗口的边界
        while right<len(s): #一直寻找到s的结尾就结束了
            c = s[right]  #扩大滑动窗口
            right += 1
            if c in need:
                window[c] += 1
                if window[c] == need[c]: #发现出现数量达到要求的字符,valid计数加一
                    valid += 1
            while valid == len(need): #开始缩紧滑动窗口的大小
                if right-left<min_len: #更新最小子串的起始索引和长度
                    start = left
                    min_len = right-left
                d = s[left]
                left += 1
                if d in need:
                    if window[d]==need[d]:
                        valid -= 1
                    window[d] -= 1
        return "" if min_len==float('inf') else s[start:start+min_len] #看是否找到了最小子串

                 

二、字符串的排列 567

https://leetcode-cn.com/problems/permutation-in-string/

给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。

换句话说,s1 的排列之一是 s2 的 子串 。

1、分析

与第一题的模板一样,只是在缩小窗口的循环控制条件处,以及valid判断处有不同。具体可以看代码及注释。

2、代码

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        need = Counter(s1)
        window = collections.defaultdict(int)
        left,right = 0,0
        valid = 0
        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):  #因为要找的是与s1排列相同的子串,所以该子串的长度应该和s1一样
                if valid == len(need): #若此时长度一样,且各个字符的数量都满足条件了,可以直接返回true,否则继续滑动窗口去寻找
                    return True
                d = s2[left]
                left += 1
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        return False

三、找到字符串中所有字母异位词 438

https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

1、分析

这道题与第二道题非常相似,只是第二题找到一个就行,这道题需要找到所有满足条件的子串。具体见代码。

2、代码

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        need = Counter(p)
        window = collections.defaultdict(int)
        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
            if 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

四、无重复字符的最长子串

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/submissions/

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

1、分析

抓住两个要点,不含重复字符,以及要找到最长的子串。具体见代码。
只需要用滑动窗口去记录遇到的字符的出现次数,一旦出现次数超过1的就立刻缩小窗口,所以每次循环时,窗口中的子串都不含有重复字符。

2、代码

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        window = collections.defaultdict(int)
        left,right = 0,0
        res = 0
        while right<len(s):
            c = s[right]
            right += 1
            window[c] += 1
            while window[c]>1: 
                # 只要有重复元素,就缩小窗口大小
                d = s[left] 
                left += 1
                window[d] -= 1
            res = max(res,right-left)
        return res


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
滑动窗口是一种常用的算法技巧,可以用于解决一类问题,其中包括一些LeetCode上的题目。通过维护一个窗口,我们可以在线性时间内解决一些需要处理连续子数组或子字符串的问题。以下是一些常见的滑动窗口问题: 1. 最小覆盖子串(Minimum Window Substring):给定一个字符串S和一个字符串T,在S中找出包含T所有字符的最小子串。 2. 字符串的排列(Permutation in String):给定两个字符串s1和s2,判断s2是否包含s1的排列。 3. 找到字符串中所有字母异位词(Find All Anagrams in a String):给定一个字符串s和一个非空字符串p,找到s中所有是p的字母异位词的子串。 4. 替换后的最长重复字符(Longest Repeating Character Replacement):给定一个只包含大写英文字母的字符串s,你可以将一个字母替换成任意其他字母,使得包含重复字母的最长子串的长度最大化。 5. 至多包含两个不同字符的最长子串(Longest Substring with At Most Two Distinct Characters):给定一个字符串s,找出至多包含两个不同字符的最长子串的长度。 以上只是几个例子,滑动窗口可以应用于更多类型的问题。在解决这些问题时,我们通常使用两个指针来表示窗口的左右边界,并根据具体问题的要求移动窗口。在每次移动窗口时,我们可以更新窗口的状态,例如统计字符出现次数、判断窗口是否满足条件等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值