滑动窗口是一种通过维护连续区间,减少重复遍历的算法技巧,常用于数组和字符串问题。它本质上是双指针的一种应用:左指针和右指针都从左向右移动,通过扩大或缩小窗口来寻找满足条件的区间。
基本思路
滑动窗口通常使用两个指针 left 和 right 表示当前窗口的左右边界,即区间 [left, right]。
右指针不断向右移动,将新的元素加入窗口;当窗口不满足题目要求时,左指针向右移动,将窗口左侧的元素移出,直到窗口重新满足条件。
由于两个指针都只向右移动,每个元素最多进入窗口一次、离开窗口一次,所以在维护窗口的操作可以在 $O(1)$ 时间内完成时,整体时间复杂度通常为 $O(n)$。
常见类型
- 固定长度窗口:窗口大小始终为
k,右指针每移动一次,左指针也同步移动,常用于计算长度为k的连续子数组或子串。 - 可变长度窗口:窗口大小会根据条件动态变化,通常先移动右指针扩大窗口,再移动左指针缩小窗口,常用于寻找满足条件的最长或最短连续区间。
- 计数窗口:使用集合、字典或数组记录窗口中的元素及其出现次数,常用于处理字符频率、重复元素和覆盖关系。
题目索引
| 题号 | 题目 | 难度 |
|---|---|---|
| 3 | 无重复字符的最长子串 | 中等 |
| 438 | 找到字符串中所有字母异位词 | 中等 |
题目记录
无重复字符的最长子串
题号:3
难度:中等
题目描述
给定一个字符串 s,请你找出其中不含有重复字符的最长子串的长度。
示例
示例 1:
1 | 输入:s = "abcabcbb" |
示例 2:
1 | 输入:s = "bbbbb" |
示例 3:
1 | 输入:s = "pwwkew" |
提示
0 <= s.length <= 10^5s由英文字母、数字、符号和空格组成
思路 $O(n)$
这道题可以使用类似快慢指针的滑动窗口来解决。使用 [slow, fast] 表示当前窗口,并始终维护这个窗口内没有重复字符。
fast 每轮向右移动一个位置,将新字符加入窗口。如果新字符已经存在于窗口中,就产生了冲突;此时移动 slow,不断移除左侧字符,直到窗口内不再包含重复的 s[fast]。
可以把这个过程理解为:用 fast 扩展窗口并制造冲突,用 slow 缩小窗口并解决冲突。每次窗口恢复为无重复状态后,使用 fast - slow + 1 更新最长长度。
由于 fast 和 slow 都只向右移动,每个字符最多被加入窗口一次、从窗口中移除一次,因此时间复杂度为 $O(n)$,空间复杂度为 $O(n)$。
解法
1 | class Solution: |
找到字符串中所有字母异位词
题号:438
难度:中等
题目描述
给定两个字符串 s 和 p,找到 s 中所有 p 的异位词子串,返回这些子串的起始索引。不考虑答案输出的顺序。
示例
示例 1:
1 | 输入:s = "cbaebabacd", p = "abc" |
示例 2:
1 | 输入:s = "abab", p = "ab" |
提示
1 <= s.length, p.length <= 3 * 10^4s和p仅包含小写字母
思路 $O(n)$
这道题使用固定长度的滑动窗口,窗口大小始终等于 p 的长度。
使用数组 p_count 统计字符串 p 中每个字符出现的次数,再使用数组 win_count 统计当前窗口中每个字符出现的次数。由于字符可能重复,不能使用 set 判断两个字符串是否由相同字符组成,而应该比较每个字符的频率是否一致。
fast 每轮向右移动,将 s[fast] 加入窗口。当窗口长度超过 p 的长度时,移动 slow,并从 win_count 中移除 s[slow]。当窗口长度恰好等于 p 的长度时,比较 win_count 和 p_count:如果两个数组相同,说明当前窗口是 p 的一个异位词,将窗口起始位置 slow 加入结果。
fast 和 slow 都只向右移动,每个字符最多加入和移出窗口一次。由于字符集固定为 26 个小写字母,比较两个频率数组的时间复杂度可以视为 $O(1)$,因此整体时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。
解法
1 | class Solution: |