滑动窗口问题总结
适用范围
1、一般是字符串或者列表
2、一般是要求最值(最大长度,最短长度等等)或者子序列
算法思想
1、在序列中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个窗口。
2、先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的序列符合要求。
3、此时,停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的序列不再符合要求。同时,每次增加 left前,都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达序列的尽头。
思路其实很简单:第 2 步相当于在寻找一个可行解,然后第 3 步在优化这个可行解,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。
Note: 因为每次right找到新的字符串后会向后移动,指向的是下一个待判断的字符,所以,如果是判断字符串的长度应该是用right-left
,而不是right-left+1。若要求是寻找符合要求的最小、短序列等,也是一样的思路,先使用滑动窗口,当符合条件时做一次优化(能否更短等)。
代码模板
func slidingWindowTemplate(s string) int {
left, right := 0, 0 // 初始化窗口的左右边界
result := 0 // 用于存储结果
window := make(map[byte]int) // 记录窗口中元素的频率,或其他窗口状态
// 遍历数组或字符串
for right < len(s) {
// 1. 扩展窗口,更新窗口状态
charRight := s[right]
window[charRight]++ // 将字符加入窗口
right++
// 2. 如果窗口不满足条件,收缩窗口
for /* 条件: 窗口不满足要求 */ {
charLeft := s[left]
window[charLeft]-- // 将左边的字符移出窗口
left++
}
// 3. 更新结果
result = max(result, right - left) // 例如,记录窗口的最大长度
}
// 4. 返回最终结果
return result
}
func max(a, b int) int {
if a > b {
return a
}
return b
}