力扣刷题记录&整理——(三)Sliding Window


前言

整理力扣刷题思路。

  • 语言:python
  • 题库:来自neetcode: link

一、预备知识

滑动窗口法

滑动窗口法是一种常用的算法策略,主要用于解决数组/列表中的子元素问题,如求取最大子序列和、最小子序列长度等问题。其主要思想是维护一个窗口,通过移动窗口来遍历整个数组/列表,从而找到问题的解。

滑动窗口法的基本步骤如下:

  1. 初始化一个窗口。窗口的大小和位置根据具体问题来定。
  2. 移动窗口并更新窗口内的信息。通常,窗口的移动是通过改变窗口的起始和结束位置来实现的。窗口内的信息可能包括窗口内元素的和、最大值、最小值等。
  3. 根据窗口内的信息来求解问题。例如,如果问题是求最大子序列和,那么就需要在每次移动窗口后,比较当前的子序列和与之前的最大子序列和。

滑动窗口法的优点是可以在线性时间复杂度内解决一些看似复杂的问题,因为每个元素只需要被访问一次或两次。但是,滑动窗口法也有其局限性,它并不适用于所有的子元素问题,只有当问题满足一定的性质(如最大子序列和问题的最优子结构性质)时,才能使用滑动窗口法。


二、解题思路

1.窗口长度不固定

3.longest-substring-without-repeating-characters

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

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0

        lookup = set()
        start = 0
        cnt = 0 #当前窗口长度
        mx = 0 #最大窗口长度

        for i in range(len(s)):
            #出现重复数时更新最大长度
            #从左端一直删除至重复的那个数
            if s[i] in lookup:
                mx = max(mx,cnt)
                while s[i] in lookup:
                    lookup.remove(s[start])
                    start += 1
                    cnt -= 1
            lookup.add(s[i])
            cnt += 1
        return max(mx,cnt)

问题图解可以参考链接: link

424.longest-repeating-character-replacement

给你一个字符串 s 和一个整数 k 。你可以选择字符串中的任一字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。

在执行上述操作后,返回 包含相同字母的最长子字符串的长度。

class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        n = len(s)
        if n < 2:
            return n
        
        l,r = 0,0
        max_cnt = 0 #当前窗口内出现次数最多的字母的计数
        hashtable = {}
        while r < n:
            hashtable[s[r]] = 1 + hashtable.get(s[r],0)
            max_cnt = max(max_cnt, hashtable[s[r]])
            
            #要替换的字符数大于k时,窗口左边界右移
            if r-l+1-max_cnt > k:
                hashtable[s[l]] -= 1
                l += 1
            r += 1
        return r-l

76.minimum-window-substring

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

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m,n = len(s),len(t)
        if not t or m<n:
            return ''
        
        #这样定义使得访问不存在的key时会返回0,而不是报错
        hashtable = collections.defaultdict(int)
        for i in range(n):
            hashtable[t[i]] += 1

        l,cnt = 0,n
        window = (0,float('inf'))

        for r in range(m):
            if hashtable[s[r]] > 0:
                cnt -= 1
            hashtable[s[r]] -= 1

            #当前窗口包含了t的所有元素
            if cnt==0:
                #不断右移l,移除窗口中的非t元素
                while l<r:
                    if hashtable[s[l]] == 0:
                        break
                    hashtable[s[l]] += 1
                    l += 1
                
                #记录当前窗口长度及左右端
                if r-l+1 < window[1]-window[0]:
                    window = (l,r+1)
                hashtable[s[l]] += 1
                cnt += 1
                l += 1

        if window[1] > m:
            return ''
        else:
            return s[window[0]:window[1]]

这题是hard难度,解题思路参考:链接: link

2.窗口长度固定

567.permutation-in-string

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

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

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if not s1:
            return True
            
        m,n = len(s1),len(s2)
        if m>n:
            return False

        hash1,hash2 = [0]*26, [0]*26
        for i in range(m):
            hash1[ord(s1[i]) - ord('a')] += 1
            hash2[ord(s2[i]) - ord('a')] += 1
        for j in range(n-m):
            if hash1==hash2:
                return True
            else:
                # 窗口整体右移一位
                hash2[ord(s2[j]) - ord('a')] -= 1
                hash2[ord(s2[j+m]) - ord('a')] += 1
        
        return hash1==hash2

239.sliding-window-maximum

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

返回 滑动窗口中的最大值 。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums or k==0:
            return []

        window = [] #队列用于存储最多一个窗口长度的数组
        #未形成窗口时
        for i in range(k):
            #保证降序排列
            while window and nums[i] > window[-1]:
                window.pop()
            window.append(nums[i])
        
        #最大值数组,加入第一个窗口内最大值
        ans = [window[0]]
        #已形成窗口后,遍历到最后
        for i in range(k,len(nums)):
            #若窗口已满,移除最左边的值
            if nums[i-k] == window[0]:
                window.pop(0)
            while window and nums[i] > window[-1]:
                window.pop()
            window.append(nums[i])
            ans.append(window[0])
            
        return ans

参考链接:链接: link

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值