【LeetCode - 滑动窗口】

双指针

  1. 字符串的题目优先考虑滑动窗口
    找字符串中的最大回文字符串,中心向两边扩散的双指针

  2. 数组有序,确定满足条件的多个元素
    有序数组中确认2个数和为目标值,left right双指针
    有序数组中确认3个数和为目标值,for遍历,left right双指针

    最接近的三数和
    在这里插入图片描述
    https://leetcode.cn/problems/3sum-closest/
    先sort排序,遍历数组,在后续数组使用双指针

滑动窗口

滑动窗口需要双指针left right作为窗的左右边界

  1. 窗口长度固定
    right - left >= s.size() 只要窗长度比目标长度大,就要缩窗,一位位移动
  2. 窗口长度变化
    求满足题目要求的最小窗口或者最大窗口,一般是valid == need.size()

小抄上的题目都是while(right < s.size())开始的滑动窗口
也有for循环中使用while(sum > target)
需要灵活变通

滑动窗口是双指针更为复杂的应用
关于子串的问题,实际上都可以使用滑动窗口解题,主要分为两种情况

  1. 子串str1在子串str2中连续分布
  2. 子串str1在子串str2中离散分布,中间穿插其它字符,要找满足要求的最短子串
    在这里插入图片描述
    定义两个map,need和windows,need统计str1中字符元素个数,windows统计当前滑窗中且元素在need中
    当前元素是need中元素,窗口右指针后移,窗中某元素个数和need中元素个数相等,valid++
    当前元素不是need中元素,窗口左指针后移,valid清0
    valid个数等于need.size(),返回true,如果窗口滑动到末尾还未确定返回false
    直接套用模板

在这里插入图片描述

string minWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;

    int left = 0, right = 0;
    int valid = 0;
    // 记录最小覆盖子串的起始索引及长度
    int start = 0, len = INT_MAX;
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 扩大窗口
        right++;
        // 进行窗口内数据的一系列更新
        if (need.count(c)) {
            window[c]++;
            if (window[c] == need[c])
                valid++;
        }

        // 判断左侧窗口是否要收缩
        while (valid == need.size()) {
            // 在这里更新最小覆盖子串
            if (right - left < len) {
                start = left;
                len = right - left;
            }
            // d 是将移出窗口的字符
            char d = s[left];
            // 缩小窗口
            left++;
            // 进行窗口内数据的一系列更新
            if (need.count(d)) {
                if (window[d] == need[d])
                    valid--;
                window[d]--;
            }                    
        }
    }
    // 返回最小覆盖子串
    return len == INT_MAX ?
        "" : s.substr(start, len);
}

在这里插入图片描述

// 此题滑动窗口的窗没有理解正确
// 窗可能添加元素到末尾,包含了很多重复的元素,这个时候需要缩窗,看是否能满足要求
// 没有缩窗的过程,"adc", "dcda"最后统计d元素有2个,而目标元素只有一个,会错误返回false(只考虑到元素没有的情况,元素一直有的情况没有考虑到)

// 滑窗模板不熟练,窗口元素的统计比较方法不需要重新写一个函数
// 每次遍历一个元素的都会统计比较,同时每次都会内层while循环判断,一旦条件满足,考虑缩窗

// 目标 map<int, char> need
//      map<int, char> window;
class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        map<int, char> need;
        map<int, char> window;
        int valid = 0;
        for (int s1Char : s1) {
            need[s1Char]++;
        }

        int right = 0;
        int left = 0;
        while (right < s2.size()) {
            char curCh = s2[right];
            right++;

            if (need.count(curCh)) {
                window[curCh]++;
                if (window[curCh] == need[curCh]) {
                    valid++;
                }
            }

            while (right - left >= s1.size()) { // right在上面已经加1,right - left就是当前窗的长度,用need的长度不准确,需要用s1的长度
                if (valid == need.size()) {
                    return true; // 获取最大值/直接返回
                }

                char cur = s2[left]; // 移出元素
                left++; // 窗口右移

                if (need.count(cur)) {
                    if (window[cur] == need[cur]) {
                        valid--;
                    }
                    window[cur]--; // 窗元素在统计之后,窗元素统计:增加窗元素在统计之前,删除窗元素,在统计之后
                }
            }
        }
        return false;
    }
};

最短数组(窗口和问题,sum类似窗口)
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的子数组。如果不存在符合条件的子数组,返回 0。
输入: [2,3,1,2,4,3], s = 7
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。
自己的版本

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // map<int, int> need;
        // map<int, int> window;
        // int valid = 0;

        int sum = 0;
        int minLen = INT_MAX;

        int left = 0;
        int right = 0;
        while (right < nums.size()) {
            sum += nums[right]; // sum实际就是窗口
            right++;

            while (sum >= target) {
                //if (sum == target) {
                    minLen = min(right - left, minLen); 
                //}
                sum -= nums[left]; // 移出窗口元素
                left++;
            }
        }
        return minLen;
    }
};

窗口和简化计算过程版本

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
		int res = INT_MAX;//用于记录滑动窗口长度
		int left = 0; //左窗口
		int right = 0; //右窗口
		int sum = 0; //窗口和大小

		for (right = 0; right < nums.size(); right++) {

			sum = sum + nums[right];
			while (sum >= target) {
				//找到符合的窗口,记录数据,并且继续缩小窗口看是否符合,不符合继续退出去循环
				res = min(right - left + 1, res);
				//缩小窗口
				sum = sum - nums[left++];
			}
		}

		return res = res == INT_MAX ? 0 : res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值