Bootstrap

算法练习题--滑动窗口算法详细解析(上)(简单题) -- 适合面试和算法竞赛 -- 滑动!

本文参考:

灵茶山艾府

题单:分享丨【题单】滑动窗口(定长/不定长/多指针) - 力扣(LeetCode)

本文主要通过Leetcode平台和上面的题单来进一步提炼出一些有意思的,值得反复练习的题目

可以反复尝试和练习,不论是面试还是打算法竞赛,还是单纯的爱好,都有或多或少的帮助

滑动窗口算法原理详解文章可以看我的文章:

定长滑动窗口算法详细解释(带例题的详细解法)-CSDN博客

不定长滑动窗口算法详细解释(带例题的详细解法)-CSDN博客

本文讲解的题目(建议先尝试一下再看题解):

(题目后面的数字表示难度分,可以直接点击链接跳转到题目)

觉得文章写的好可以点个赞,下个星期争取更新中等题和困难题的题解,还有二分查找算法

最近codeforces比赛也挺多的,会有点忙

题一:

567. 字符串的排列 - 力扣(LeetCode)

题目大意和思路:

两个字符串,判断s2是否包含s1的排列

也就是说字符串s2中是否存在一段连续子字符串满足:

长度与s1相等,且所含有字母跟s1一样

可以维护一个长度等于s1的窗口来遍历s2

先统计开始窗口的字符

窗口开始滑动,维护出来的字符和进入的字符

统计字符数量可以用哈希表来处理

注意:如果使用哈希表处理,那么当窗口内某元素个数为0时候,要删除这个键

哈希表做法(Python):

class Solution:
    def checkInclusion(self, s1: str, s: str) -> bool:
        k, n = len(s1), len(s)
        if k > n:
            return False
        
        map1 = defaultdict(int)
        map2 = defaultdict(int)
        
        for c in s1:
            map1[c] += 1
        
        for i in range(n):
            map2[s[i]] += 1
            
            if i >= k:
                map2[s[i - k]] -= 1
                #如果数量等于0,则删除这个键
                if map2[s[i - k]] == 0:
                    del map2[s[i - k]]
            
            if map1 == map2:
                return True
        
        return False

优化:

题目条件明确字符串中只含有小写字母,那么可以用一个长度为26的数组进行计数

可以把空间复杂度降低到常数

优化代码(Python):

class Solution:
    def checkInclusion(self, s1: str, s: str) -> bool:
        k, n = len(s1), len(s)
        if k > n:
            return False
        
        cnt1 = [0] * 26
        cnt2 = [0] * 26
        
        for c in s1:
            cnt1[ord(c) - ord('a')] += 1
        
        for i in range(n):
            cnt2[ord(s[i]) - ord('a')] += 1
            
            if i >= k:
                c = s[i - k]
                cnt2[ord(c) - ord('a')] -= 1
                
            if cnt1 == cnt2:
                return True
      
        return False

更多语言(C++,Java,Go)描述:

567. 字符串的排列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/permutation-in-string/solutions/2873814/567-zi-fu-chuan-de-pai-lie-hua-dong-chua-pgzn/

题二:

2090. 半径为 k 的子数组平均值 - 力扣(LeetCode)

题目大意和思路:

题目中定义了一个半径k的子数组

可以得到这个子数组的长度为 2 * k + 1

然后题目又定义了一个平均值

就是以一个下标为i的半径为k的子数组中所有数字的平均值

很明显,下标 i 前或后不足 k 个元素,此时平均值 是 -1

然后题目要求就是:

返回一个长度为 n 的数组 avgs ,其中 avgs[i] 是以下标 i 为中心的子数组的

半径为 k 的子数组平均值

(题目中说的截断式 整数除法,意思就是直接用/即可,python 用 //)

嗯...应该很好理解
 

很明显这是一道长度为2 * k + 1的滑动窗口题目,计算滑动窗口的平均值即可

首先,数组前k个和后k个肯定是-1

当这个半径为k的子数组大于原来数组长度n的时候,答案就全是-1

然后只需要计算下标从k 到 n - k - 1这个区间内的值

先求出第一个窗口的和win_sum,也就是从0 到 2 * k + 1

然后滑动窗口,win_sum 减去离开的值,加上进入的值

更新答案即可

代码(Python):
 

class Solution:
    def getAverages(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        res = [-1] * n
        win_size = 2 * k + 1

        if win_size > n:
            return res
        
        win_sum = sum(nums[:win_size])
        
        res[k] = win_sum // win_size

        for i in range(k + 1, n - k):
            win_sum += nums[i + k] - nums[i - k - 1]
            res[i] = win_sum // win_size
        
        return res

更多语言(C++,Java,Go)描述:

2090. 半径为 k 的子数组平均值 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/k-radius-subarray-averages/solutions/2874523/hua-dong-chuang-kou-jian-dan-yi-dong-ti-omw59/

题三:

904. 水果成篮 - 力扣(LeetCode)

题目大意和思路:

现在有一排树,用一个数组表示,数组上的每一个值表示树的种类,不同的值表示不同的种类

现在有两个篮子,每个篮子只能放特定的水果

从前走到后,问最多能有摘多少棵树的水果

然后题目加了一个限定:

一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

也就是说你必须在连续的一段区间内进行采摘,且只有两个篮子,那就只能采摘两个类型的水果

根据题目意思,就是在数组上找一个区间的长度最大值,满足这个区间只有两种不同的数字

就是不定长滑动窗口求区间长度最大值

可以使用哈希表来统计窗口内元素的个数

用两个指针维护窗口的左右端点,右指针移动,元素进入窗口

更新哈希表

当窗口内元素不满足要求的时候

就移动左指针,减小窗口大小,并更新哈希表

然后更新在这个过程中窗口的最大值即可

代码(Python):

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        res, l = 0, 0
        cnt = defaultdict(int)

        for r in range(len(fruits)):
            cnt[fruits[r]] += 1

            while len(cnt) > 2:
                cnt[fruits[l]] -= 1
                if cnt[fruits[l]] == 0:
                    del cnt[fruits[l]]
                l += 1
            
            res = max(res, r - l + 1)

        return res

更多语言(C++,Java,Go)描述:

904. 水果成篮 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/fruit-into-baskets/solutions/2874690/hua-dong-chuang-kou-jian-dan-yi-dong-ti-9zvts/

题四:

2024. 考试的最大困扰度 - 力扣(LeetCode)

题目大意和思路:

有一个字符串,只含有T和F

现在给一个k,表示操作次数,你可以进行以下操作k次:

把一个字符变成T或者F

现在要求一段连续的,最长的,只含有一个字符的,子字符串长度

题目所要找的连续子字符串可以理解成k个T或者F加上尽可能多的F或者T

也就是说一个连续的区间内,这个区间满足最多k个字符T(或者F)和尽可能多的F(或者T)

那么就是不定长滑动窗口求区间长度最大值

这里有两种情况,第一种是最多k个F,第二种是最多k个T

那么可以遍历两遍字符串

使用哈希表来维护窗口内的字符串数量

定义一个左端点,滑动窗口,元素进入窗口,更新哈希表

当窗口内元素不满足要求的时候

就移动左指针,减小窗口大小,并更新哈希表

然后更新在这个过程中窗口的最大值即可

求出两种情况的最大值,输出这两个的最大值即可

代码(Python):

class Solution:
    def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
        left = 0
        x1, x2 = 0, 0
        cnt = defaultdict(int)

        # 计算最多连续"F"的最长子序列
        for right, c in enumerate(answerKey):
            cnt[c] += 1
            while cnt['F'] > k:
                cnt[answerKey[left]] -= 1
                if cnt[answerKey[left]] == 0:
                    del cnt[answerKey[left]]
                left += 1
            x1 = max(x1, right - left + 1)

        # 重置left和cnt以计算最多连续"T"的最长子序列
        left = 0
        cnt.clear()

        for right, c in enumerate(answerKey):
            cnt[c] += 1
            while cnt['T'] > k:
                cnt[answerKey[left]] -= 1
                if cnt[answerKey[left]] == 0:
                    del cnt[answerKey[left]]
                left += 1
            x2 = max(x2, right - left + 1)

        return max(x1, x2)

可以把这个写成一个方法进行优化:

优化代码(Python):

class Solution:
    def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
        def function(c):
            left = 0
            cnt = defaultdict(int)
            res = 0
            for right in range(len(answerKey)):
                cnt[answerKey[right]] += 1
                while cnt[c] > k:
                    cnt[answerKey[left]] -= 1
                    left += 1
                res = max(res, right - left + 1)
            return res

        return max(function("T"), function("F"))

更多语言(C++,Java,Go)描述:

2024. 考试的最大困扰度 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/maximize-the-confusion-of-an-exam/solutions/2874810/hua-dong-chuang-kou-jian-dan-yi-dong-ti-uyg8e/

;