滑动窗口问题总结

适用范围

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
}