滑动窗口刷题总结

LeetCode题单

1. 定长滑窗

定长滑窗比较简单,每次都是单个元素的进出,核心就是快速更新窗口内元素的信息。
模板663.子数组最大平均数I

double findMaxAverage(vector<int>& nums, int k) {
    int vowel = 0;
    double ans = -1e9-1;
    int n = nums.size(), l;
    for(int r = 0; r < n ; r++){
        // 右入
        vowel += nums[r];
        l = r-k+1;
        if(l < 0) continue;
        // 更新
        ans = max(ans, (double)vowel);
        // 左出
        vowel -= nums[l];
    }
    return ans/k;
}

2. 不定长滑窗

2.1 求最长/最大

题目一般包含至多、小于等于等要求
模板2958.最多K个重复元素的最长子数组
思路:枚举右端点r,维护左端点l,当维护结束时,子数组[l,r]是以r为右端点,满足题目要求的最长子数组。

int maxSubarrayLength(vector<int>& nums, int k) {
    int ans = 0, l = 0;
    map<int, int> cnt;
    for(int r = 0; r < nums.size(); r++){
        // 右入
        cnt[nums[r]]++;
        // 左出
        while(cnt[nums[r]] > k){
            cnt[nums[l]]--;
            l++;
        }
        ans = max(ans, r-l+1);
    }
    return ans;
}

2.2 求最短/最小

题目一般包含至少、大于等于等要求。本质与求最长没有区别,只是判定条件不同,一般会比最长的判定条件复杂一些。
模板209.长度最小的子数组
思路:枚举右端点r,维护左端点l,当维护结束时,子数组[l,r]是以r为右端点,满足题目要求的最短子数组。

int minSubArrayLen(int target, vector<int>& nums) {
    int n = nums.size(), ans = n+1, l = 0, sum = 0;
    for(int r = 0; r < n; r++){
        // 右入
        sum += nums[r];
        // 左出
        while(sum - nums[l] >= target) sum -= nums[l++];
        if(sum >= target) ans = min(ans, r-l+1);
    }
    return ans > n ? 0 : ans;
}

2.3 求子数组数量

2.3.1 越长越合法型

题目一般包含至少、大于等于等要求,是2.2求最短/最小子数组的变形,一般会写ans+=l。
模板3325.字符至少出现K次的子字符串I
思路:枚举右端点r,维护左端点l,当维护结束时,子数组[l-1,r]是以r为右端点,满足题目要求的最短子数组,则以0、1、...、l-1为左端点的子数组都满足要求,ans+=l。

int numberOfSubstrings(string s, int k) {
    int n = s.size(), l=0, ans = 0;
    vector<int> cnt(26); 
    for(int r = 0; r < n; r++){
        // 右入
        cnt[s[r]-'a']++;
        // 左出
        while(cnt[s[r]-'a'] >= k) cnt[s[l++]-'a']--;
        ans += l;
    }
    return ans;
}

2.3.2 越短越合法型

题目一般包含至多、小于等要求,是2.1求最长/最大子数组的变形,一般会写ans+=r-l+1。
模板2302.统计得分小于K的子数组
思路:枚举右端点r,维护左端点l,当维护结束时,子数组[l,r]是以r为右端点,满足题目要求的最长子数组,则以l、l+1、...、r为左端点的子数组都满足要求,ans+=r-l+1。
注意内嵌循环判定条件一般需要加上l <= r,否则容易越界

long long countSubarrays(vector<int>& nums, long long k) {
        long long sum = 0, ans = 0;
        int l = 0;
        for(int r = 0; r < nums.size(); r++){
            // 右入
            sum += nums[r];
            // 左出
            while(sum*(r-l+1) >= k && l <= r) sum -= nums[l++];
            ans += r-l+1;
        }
        return ans;
    }

2.3.3 恰好等于型

可以视情况转化为越长越合法型或越短越合法型。
转化为越长越合法型模板992.K个不同整数的子数组

int f(vector<int>& nums, int k){
    int n = nums.size(), l = 0;
    int sum = 0, ans = 0;
    vector<int> cnt(n+1);
    for(int r = 0; r < n; r++){
        sum += cnt[nums[r]] == 0;
        cnt[nums[r]]++;
        while(sum >= k){
            cnt[nums[l]]--;
            sum -= cnt[nums[l]] == 0;
            l++;
        }
        ans += l;
    }
    return ans;
}
int subarraysWithKDistinct(vector<int>& nums, int k) {
    // 转化为 越长越合法问题
    // 不同整数个数 >= k
    return f(nums, k) - f(nums, k+1);
}

越短越合法型模板930.和相同的的二元子数组

int f(vector<int>& nums, int goal){
    int ans = 0, n = nums.size();
    int sum = 0, l = 0;
    for(int r = 0; r < n; r++){
        sum += nums[r];
        while(sum >= goal && l <= r) sum -= nums[l++];
        ans += r-l+1;
    }
    return ans;
}
int numSubarraysWithSum(vector<int>& nums, int goal) {
    // 转化为 越短越合法问题
    // 求窗口内和sum <= goal的子数组个数
    return f(nums, goal+1)-f(nums, goal);
}
posted @ 2025-04-02 16:57  zerolt  阅读(42)  评论(0)    收藏  举报