AI summary
type
status
date
slug
summary
category
tags
icon
password
关于双指针的快慢指针和左右指针的用法,可以参见前文 双指针技巧秒杀七道数组题目,本文就解决一类最难掌握的双指针技巧:滑动窗口技巧。并总结出一套框架,可以保你闭着眼睛都能写出正确的解法。
滑动窗口算法技巧主要用来解决子数组问题,比如让你寻找符合某个条件的最长/最短子数组。
如果用暴力解的话,你需要嵌套 for 循环这样穷举所有子数组,时间复杂度是 。
滑动窗口算法技巧的思路也不难,就是维护一个窗口,不断滑动,然后更新答案,该算法的大致逻辑如下:
基于滑动窗口算法框架写出的代码,时间复杂度是 O(N),比嵌套 for 循环的暴力解法效率高。
肯定有读者要问了,你这个滑动窗口框架不也用了一个嵌套 while 循环?为啥复杂度是 O(N) 呢?简单说,指针left, right不会回退(它们的值只增不减),所以字符串/数组中的每个元素都只会进入窗口一次,然后被移出窗口一次,不会说有某些元素多次进入和离开窗口,所以算法的时间复杂度就和字符串/数组的长度成正比。
滑动窗口并不能穷举出所有子串。要想穷举出所有子串,必须用那个嵌套 for 循环。
然而对于某些题目,并不需要穷举所有子串,就能找到题目想要的答案。滑动窗口就是这种场景下的一套算法模板,帮你对穷举过程进行剪枝优化,避免冗余计算。
76. 最小覆盖子串
给你一个字符串s、一个字符串t。返回s中涵盖t所有字符的最小子串。如果s中不存在涵盖t所有字符的子串,则返回空字符串""。注意:
- 对于
t中重复字符,我们寻找的子字符串中该字符数量必须不少于t中该字符数量。
- 如果
s中存在这样的子串,我们保证它是唯一的答案。进阶:你能设计一个在o(m+n)时间内解决此问题的算法吗?
滑动窗口算法的思路是这样:
1、我们在字符串
S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。为什么要「左闭右开」区间?方便边界处理。
2、我们先不断地增加
right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 T 中的所有字符)。3、此时,我们停止增加
right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。4、重复第 2 和第 3 步,直到
right 到达字符串 S 的尽头。这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,就好像一条毛毛虫,一伸一缩,不断向右滑动,这就是「滑动窗口」这个名字的来历。
算法过程如下图:

https://assets.leetcode-cn.com/solution-static/76/76_fig1.gif (Notion-Next 的图片视频等文件好像显示出 bug 了,放个链接在这)
代码如下:
更新答案的部分
if right - left < length: 为什么不能放到第二层 while 外面,缩到最小再更新,节省时间?因为要保证
valid == len(need) 才能更新答案,如果放外面,那么在第一次扩窗口时 length 会被赋 0,显然不行。使用 Java 的读者请注意:对 Java 包装类进行比较时要尤为小心,Integer,String等类型应该用equals方法判定相等,而不能直接用等号==,否则会出错。所以在缩小窗口更新数据的时候,不能直接写为window.get(d) == need.get(d),而要用window.get(d).equals(need.get(d))
567. 字符串的排列
给你两个字符串s1和s2,写一个函数来判断s2是否包含s1的排列。如果是,返回true;否则,返回false。换句话说,s1的排列之一是s2的 子串 。
这种题目,是明显的滑动窗口算法,相当给你一个
S 和一个 T,请问你 S 中是否存在一个子串,包含 T 中所有字符且不包含其他字符?与上一题的区别在于窗口内的子串不能包含多余的字符,因此窗口的大小可以固定为
len(s2) ,所以缩小窗口的 while 可以换成 if,因为除了第一次外,每次都是扩一格,缩一格。438. 找到字符串中所有字母异位词
给定两个字符串s和p,找到s中所有p的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
这个所谓的字母异位词,不就是排列吗,搞个高端的说法就能糊弄人了吗?相当于,输入一个串
S,一个串 T,找到 S 中所有 T 的排列,返回它们的起始索引。和上一题基本一模一样,只是有多解,秒了:
3. 无重复字符的最长子串
给定一个字符串s,请你找出其中不含有重复字符的 最长 子串 的长度。
这个题终于有了点新意,不是一套框架就出答案,不过反而更简单了,need 都不需要了,稍微改一改框架就行了:
这里和之前不一样,要在收缩窗口完成后更新
res,因为窗口收缩的 while 条件是存在重复元素,换句话说收缩完成后一定保证窗口中没有重复嘛。