力扣1004滑动窗口超详细解答(转载)

  1. 最大连续1的个数 III
    给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。
    返回仅包含 1 的最长(连续)子数组的长度。

    在这里插入图片描述

解题思路
首先考虑本问题的最直观朴素的想法:枚举出每一个子串,逐个验证其有效性并且更新最大值
这种做法的时间复杂度是O(n^3),在这一数据规模下是一定会超时的
超时的原因是做了非常多重复的验证计算,且枚举了非常多没有意义的串
那么基于我们在这类连续子数组最值性质问题的经验,使用双指针(滑动窗口)会是一个非常好的想法

双指针解法
双指针解法的重点在于维护两个指针:
设置两个指针(表现为数组下标值),左指针指向当前子串左边界,右指针指向当前子串右边界,我们通过某种方法使得左右指针之间的当前子串符合要求:即 ‘0’ 的数目小于等于 K
又:我们很容易知道,最长串一定出现在串内 ‘0’ 的数目饱和(如果’0’比较多)或者最大(如果 K 比较大)的情况下,因此我们需要尽可能地把右指针右移

那么固定左指针时,什么情况下可以使右指针右移呢?
转化次数K有剩余 或
右指针的下一位是’1’
如此移动右指针,即可找到每一个左指针所对应的最长有效串,而且右指针指向的元素的下一位一定是’0’
那么我们可以把每一个数组元素作为左指针,以上述方式枚举出最长有效串,然后返回结果

但是还是不够快

不够快的原因还是做了重复计算,在左指针变化时,右指针从左指针处开始右移,没有有效利用上一次操作的结果,造成了大量没有必要的操作
那么如何避免重复计算呢?

我们的关键在于刚刚提到的“0数饱和”
即:可能成为最长串的串,它内部’0’的数量一定是饱和或者最大的
那么我们可以通过某种操作来使得在左指针右移之后,我们立即就能获得新左指针对应的’0’数饱和串/'0’数最大串

首先我们考察左指针右移一位时的情况:
首先我们需要清楚一个事实:当右指针到达串尾部时,左指针没有任何必要继续右移,因为左指针右移的话子串的长度一定会减小,并且饱和性质无法保证
也就是说,左指针右移的条件是:右指针没有触碰串的右边界
那么我们会得到一个推论:当左指针需要右移时,串中的’0’一定是饱和的而不是最大的(“饱和”指子串中0的数目与K相等,不可以再转化0为1;“最大”指整个串中所有的0均已被转化为1)既饱和又最大的情况也不需要右移

得到这个推论,我们就可以来考虑左指针右移时的情况:

当前左指针指向的元素是’0’时:当左指针右移时子串内的’0’数减一,为了维护其饱和性质,我们刚刚提到“右指针指向的元素的下一位一定是’0’”,所以右指针一定可以右移至少一位(如果右移之后的下一位是’1’则可以继续右移)
当前左指针指向的元素是’1’时:当左指针右移时子串内的’0’数不变,由于我们维护了每一个子串的饱和性质,右指针不可以右移
每一次左指针移动及之后右指针移动的一系列操作结束之后,更新最大值
通过以上的操作,遍历直到右指针触碰数组右边界,即可得到全局的最大符合条件串的长度

作者:amachi-inori
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/c-1004-hua-dong-chuang-kou-chao-xiang-xi-jie-shuo-/
来源:力扣(LeetCode)
在这里插入图片描述

//天知いのり 2020.5.24
class Solution {
public:
    int longestOnes(vector<int>& A, int K) {
        int left(0), right(0);
        int max(0);
        while (left <= right && right < A.size()) {
            while (right < A.size() && (A[right] || K != 0)) {
                if (!A[right]) K--;
                right++;
            }
            max = std::max(right - left, max);
            if (A[left]) left++;
            else left++, right++;
        }
        return max;
    }
};

作者:amachi-inori
链接:https://leetcode-cn.com/problems/max-consecutive-ones-iii/solution/c-1004-hua-dong-chuang-kou-chao-xiang-xi-jie-shuo-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

my solution

class Solution {
public:
	int longestOnes(vector<int>& A, int K) {
		int len = A.size();
		int left = 0, right = 0;//窗体起始点&最终点下一个位置
		int max_lenth = 0;
		int cur_lenth = 0;
		int nums = 0;//记录0的个数
		while (left <= right && right < len) {
			while (right < len &&(nums < K || A[right]) ) {//当窗体内0的个数小于K的时候就扩增窗体
				if (A[right] == 0)
					nums++;
				right++;
			}
			//出循环之后此时窗体内0的数目为K
			cur_lenth = right - left;//当前窗口的大小
			if (cur_lenth > max_lenth)
				max_lenth = cur_lenth;
			if (!A[left])
				left++, right++;//当窗口最左端为0,右移之后又可以继续向左扩大窗口
			else
				left++;//若最左边是1,那么移动之后会少一个1,右侧暂且不动
		}
		return max_lenth;
	}
};

### 力扣 1456 题:定长子串中元音的最大数目(滑动窗口算法 C++ 实现) 这道题的核心思想是使用**滑动窗口**算法来高效地计算每个长度为 `k` 的子串中包含的元音字母数量,并找出最大值。 #### 解法分析 题目要求在字符串 `s` 中找到长度为 `k` 的所有子串中,元音字母(a, e, i, o, u)数量最多的那个子串中的元音数量。 滑动窗口的基本思路如下: 1. **初始化窗口**:首先统计初始窗口(即从索引 `0` 到 `k-1` 的子串)中元音的数量。 2. **滑动窗口移动**:从索引 `k` 开始,每次向右移动一个字符,将新进入窗口的字符判断是否为元音,同时将离开窗口的字符也进行判断并更新计数。 3. **维护最大值**:在整个过程中维护一个变量,记录当前窗口中元音的最大数量。 #### C++ 实现代码 ```cpp #include <string> #include <algorithm> using namespace std; class Solution { public: int maxVowels(string s, int k) { int n = s.size(); int num = 0; // 初始化第一个窗口的元音数量 for (int i = 0; i < k; i++) { if (isVowel(s[i])) { num++; } } int res = num; // 滑动窗口遍历整个字符串 for (int i = k; i < n; i++) { if (isVowel(s[i])) { num++; // 新加入窗口的字符是元音 } if (isVowel(s[i - k])) { num--; // 离开窗口的字符是元音 } res = max(res, num); } return res; } private: bool isVowel(char c) { return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; } }; ``` #### 时间复杂度与空间复杂度 - **时间复杂度**:`O(n)`,其中 `n` 是字符串 `s` 的长度。每个字符最多被访问两次(一次加入窗口,一次移出窗口),因此整体复杂度为线性。 - **空间复杂度**:`O(1)`,仅使用了常量级别的额外空间[^5]。 #### 优化点 - 在判断字符是否为元音时,可以使用哈希集合(如 `unordered_set<char>`)来提高查找效率,但在这个实现中直接使用条件判断更为简洁高效。 - 当窗口内元音数量达到最大可能值 `k` 时,可以直接返回结果,提前结束循环。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值