滑动窗口框架

从整体到细节,自顶向下,从抽象到具体的框架思维是通用的,不只是学习数据结构和算法,学习其他任何知识都是高效的。

# 双指针技巧

不论是链表还是数组,双指针的技巧都可以使用。

双指针的技巧可以分为两类,一类是快慢指针,一类是左右指针。前者主要解决链表中的问题,比如链表中是否有环的判定并给出环的起点位置,寻找链表中点,寻找链表倒数第 k 个元素等。后者主要解决数组(或字符串)问题,比如二分查找,有序数组的两数之和,反转数组以及非常经典的滑动窗口算法。

# 滑动窗口算法

滑动窗口算法可以说是双指针的最高境界了,该算法的大致框架如下。

void SlidingWindow(string s, string t)
{
    unordered_map<char, int> need, window;
    for(char c: t) need[c] ++; // 添加元素给目标集合

    int left = 0, right = 0; // 左闭右开区间
    int valid = 0; // 记录有效元素的个数
    while(right < s.size())
    {
        // c 为即将加入窗口的字符
        char c = s[right];
        ++ right;

        // 1. 窗口内的数据更新
        // ...

        // 2. 窗口何时收缩
        while(window needs shrink)
        {
            // 3. 判断是否满足 need 条件
            // ...

            // d 是即将移出窗口的字符
            char d = s[left];
            ++ left;

            // 4. 窗口内的数据更新
            // ...
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

只要回答好代码框架中的四个问题,那么这道滑动窗口的问题基本就 💯

以后只要遇到子串问题,基本都可以使用这个框架完美解决!注意子串与子序列的不同,比如 aceabcde 的子序列,但不是子串,abcabcde 的子串,同时也是其子序列。