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

120 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

一、最小覆盖子串 76

leetcode-cn.com/problems/mi…

给你一个字符串 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

leetcode-cn.com/problems/pe…

给你两个字符串 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

leetcode-cn.com/problems/fi…

给定两个字符串 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

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

leetcode-cn.com/problems/lo…

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

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