数据结构算法刷题(8)滑动窗口

滑动窗口本质上是解决子串问题!!!!

滑动窗口的算法流程如下:

1、左闭右开区间[left, right)称为一个「窗口」,先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。 

2、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同时,每次增加left,我们都要更新一轮结果。

3、重复第 2 和第 3 步,直到right到达字符串S的尽头。

举例说明:

 

重点:

在整个滑动过程中,我们涉及到的变量有:

字典:need ,将t中所有元素出现的个数存入字典中。

need = collections.defaultdict(int)
for c in t:
    need[c]+=1

字典:window,将滑动窗口中符合t中元素的个数存入字典中,初始化全为0

window = dict(zip(t, [0] * len(t)))

窗口变量:起始下标 start = 0 和当前窗口的最小长度 minlen = len(s) + 1,给minlen一个绝对达不到的长度

滑动变量:左下标left, 右下标right,以及判断何时开始收缩左下标的变量valid = 0

思路:

因为区间是左闭右开,所以跳出循环的条件是right == len(s), 也就是说,进入循环的条件是

while right < len(s):

进入循环后,首先移动right,当下标所在元素s[right]是在need中出现了,window中对应的元素值加一,如果加一后,need对应的元素值和window的值相等,说明满足了某个元素的出现次数,让valid加一。

if s[right] in need:

        window[s[right]] += 1

        if window[s[right] == need[s[right]]:

                valid += 1

right += 1

当窗口内的全部元素出现次数都满足要求,就要开始收缩left,直到窗口不满足要求。同时要对start和minlen进行维护,窗口的收缩和扩张一直到s的结尾,但是最后输出的元素要满足最小子串的要求。所以,每当出现一次满足窗口,我们都要和原来的minlen比较一次。

if right - left < minlen:

        start = left

        minlen = rigth - left

while valid == len(need):

        if s[left] in need:

                if window[s[left]] == need[s[left]]:

                        valid -= 1

                window[s[left]] -= 1

        left += 1

最后跳出循环之后,我们要进行一个判断,如果minlen的值没有改变,证明根本没有满足的窗口出现过,返回'' '',否则,我们返回s[start:start+minlen]

return s[start:start+minlen] if minlen != len(s) + 1 else ' '

以下是完整代码:

class Solution:

    def minWindow(self, s: str, t: str) -> str:

        import collections

        need = collections.defaultdict(int)

        for c in t:

            need[c] += 1

        window = dict(zip(t,[0]*len(t)))

        right = 0

        left = 0

        valid = 0

        start = 0

        minlen = len(s) + 1

        while right < len(s):

            if s[right] in need:

                window[s[right]] += 1

                if window[s[right]] == need[s[right]]:

                    valid += 1

            right += 1

            while valid == len(need):

                if right - left < minlen:

                    start = left

                    minlen = right - left

                if s[left] in need:

                    if window[s[left]] == need[s[left]]:

                        valid -= 1

                    window[s[left]] -= 1

                left += 1

        return s[start:start+minlen] if minlen != len(s) + 1 else ''

把题目拆解一下,就是找到包含s1的最小子串,并且长度要和s1相等,如果没有就返回False

全部代码:

class Solution:

    def checkInclusion(self, s1: str, s2: str) -> bool:

        import collections

        need = collections.defaultdict(int)

        for c in s1:

            need[c] += 1

        window = dict(zip(s1,[0]*len(s1)))

        right = 0

        left = 0

        valid = 0

        start = 0

        minlen = len(s2) + 1

        while right < len(s2):

            if s2[right] in need:

                window[s2[right]] += 1

                if window[s2[right]] == need[s2[right]]:

                    valid += 1

            right += 1

            while valid == len(need):

                if right - left < minlen:

                    start = left

                    minlen = right - left

                if s2[left] in need:

                    if window[s2[left]] == need[s2[left]]:

                        valid -= 1

                    window[s2[left]] -= 1

                left += 1

        return True if minlen != len(s2) + 1 and len(s2[start:start+minlen]) == len(s1) else False

 

 把题目拆解一下就是,找到所有的匹配的子串,并且当子串的长度与p相同时,输出其起始索引。

全部代码:

class Solution:

    def findAnagrams(self, s: str, p: str) -> List[int]:

        import collections

        need = collections.defaultdict(int)

        for i in p:

            need[i] += 1

        window = dict(zip(p,[0]*len(p)))

        start = []

        length = len(p)

        right = 0

        left = 0

        valid = 0

        while right < len(s):

            if s[right] in need:

                window[s[right]] += 1

                if window[s[right]] == need[s[right]]:

                    valid += 1

            right += 1

            while valid == len(need):

                if right - left == length:

                    start.append(left)

                if s[left] in need:

                    if window[s[left]] == need[s[left]]:

                        valid -= 1

                    window[s[left]] -= 1

                left += 1

        return start

由于没有事先知道的need,所以我们只需要维护窗口window一个字典,要求无重复字符的最长子串,那我们把全部字符存入字典中,初始值为0. right开始移动,每次移动都把window中对应元素的键值加一,如果该值大于1,证明有重复元素,让valid = 1。开始移动left,将window中对应的元素键值减一,而且要维护left的移动条件,如果window中对应元素大于1,valid  -= 1。 移动完left之后(跳出了第二个循环),开始计算此时的window长度,选取最大长度作为length。最后返回length。

class Solution:

    def lengthOfLongestSubstring(self, s: str) -> int:

        window = dict(zip(set(s),[0]*len(set(s))))

        rigth = 0

        left = 0

        length = 0

        valid = 0

        while rigth < len(s):

            window[s[rigth]] += 1

            if window[s[rigth]] > 1:

                valid = 1

            rigth += 1

            while valid > 0:

                if window[s[left]] > 1:

                    valid -= 1

                window[s[left]] -= 1

                left += 1

            if rigth - left > length:

                length = rigth - left

        return length 

用一个固定的滑动窗口,窗口的左边界是left = 0,右边界是right = length - 1,固定right之后,让left每次移动len(word[0])个长度,同样是window中元素的值与need中元素的值相同时,记录下这个窗口的左边界。然后让right+=1,重新确定窗口的左边界left = right - length + 1,再次移动。直到right == len(s)。

以下是全部代码:

class Solution:

    def findSubstring(self, s: str, words: List[str]) -> List[int]:

        import collections

        need = collections.defaultdict(int)

        for i in words:

            need[i] += 1

        window = dict(zip(words, [0] * len(words)))

        length = len(words[0])*len(words)

        right = length - 1

        left = 0

        valid = 0

        start = []

        t = s + '0'*(len(s)%len(words[0])) + '0'*len(words[0]) #这里是为了补全s,并且加长,防止下标出界。但是我感觉题目用的示例都没有这个问题,可写可不写。

        while right < len(s):

            left = right - length + 1

            valid = 0

            window = dict(zip(words, [0] * len(words))) #每次重新建立窗口,都要将valid和窗口元素值置0,同时重新计算左边界起始位置。

            while left <= right:

                if t[left:left+len(words[0])] in need: #满足条件时

                    window[t[left:left+len(words[0])]] += 1

                    if window[t[left:left+len(words[0])]] == need[t[left:left+len(words[0])]]:

                        valid += 1

                        if valid == len(need):#攒齐一波

                            start.append(right - length + 1)

                            valid = 0

                            window = dict(zip(words, [0] * len(words)))

                            break  #每次窗口的大小就是一个正确答案的大小,所以如果该窗口是,那就一定是,不是,那就一定不是,确定之后就跳出到下一个窗口,并且都置0.

                left += len(words[0]) #窗口内每次滑动一个单词大小长度

            right += 1 #窗口外每次滑动一个字母

        return start

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值