滑动窗口算法

  • content {:toc}

滑动窗口

O(n) 时间内解决 子串, 子数组问题

  • right 表示待处理的节点
  • 窗口其实就是 [left, right), 窗口大小是 left- right
  • right 向右寻找可行解,left 向右寻找最优解
  • 等价于枚举左端点
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#-----------------V1----------------
while right < s.size():
    # 处理s[right]
    ++right;
    
    while( shirink && l<r ):
        # 满足 题目 条件的在这里更新
        updata res
        # 处理left
        ++left

#-----------------V2----------------     
while right < s.size():
    # 处理s[right]
    ++right;
    
    while(!shirink  && l<r ):
        # 处理left
        ++left    
    #不满足条件的在这里更新
    updata res

满足不满足条件的意思是:

  • 随着 r++, 你所想要的目标(求和, 求积) 是怎么进行变化, 是向着你想要的目标走还是怎样

满足题目条件—循环内更新

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。如果不存在符合条件的子数组,返回 0

1
2
3
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int minSubArrayLen(int target, vector<int>& nums) {
    int len = nums.size();
    int l =0, r=0;
    int sum = 0;
    int minlen = INT_MAX;
    while(r< len){
        sum+= nums[r++];

        while(sum >= target){
            minlen = min(minlen, r-l); 
            sum -= nums[l++];

        }
    }
    return minlen == INT_MAX ? 0: minlen;

}
  • 在本题中, 随着 r++, 目标和越来越大, 是向着你想要的目标走, 因此用的是 V1, 在循环内更新 结果

不满足题目条件—循环外更新

713. 乘积小于 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

1
2
3
4
输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
 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
class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        // 返回子数组的数目
        int res = 0;
        int l =0, r= 0;
        int sum =1;
        int len = nums.size();

        while(r<len){
            sum *= nums[r];
            ++r;
            while(sum >= k && l<r){
                sum /= nums[l];
                l++;

            }
            // [l, r) 内, 且右边断点为 r 的子数组共 (r-l)个
            res += (r-l);
        }

        return  res;

    }
};
  • 在本题中, 随着 r++, 目标积 越来越大, 题目要求是要我们 小于 k , 因此用的是 V2, 在循环外更新 结果

3. 无重复字符的最长子串( DP )

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

1
2
3
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
 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
// -----------------------------V2.0----------------------------------
int lengthOfLongestSubstring(string s) {
    unordered_map<char, int> mp;
    int l =0, r=0;
    int len = s.size();
    int res = 1;

    // 处理特殊情况
    if(!len) {
        return 0;
    }
    while(r<len){
        char c = s[r];
        r++;
        mp[c]++;
        while((r-l) > mp.size()){
            c = s[l++]; 
            mp[c]--;
            if(0==mp[c]){
                mp.erase(c);

            }
        }
        res = max(res, r-l);
    }
    return res;
}
使用 Hugo 构建
主题 StackJimmy 设计