最长连续1的个数Ⅲ
题目链接:1004. 最大连续1的个数 III - 力扣(LeetCode)
给定一个二进制数组
nums
和一个整数k
,如果可以翻转最多k
个0
,则返回 数组中连续1
的最大个数 。示例 1:
输入:
nums = [1,1,1,0,0,0,1,1,1,1,0]
,K = 2
输出:6
解释:[1,1,1,0,0,1 ,1,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 6。
思路解析:
题目说明最多翻转k个0,代表翻转0的个数可以有[0, k]个,所以有两种情况:
-
翻转个数在区间[0, k]中
-
翻转个数大于
k
个时只能翻转k
个
所以,本题可以将原有问题转化为数组中0的个数不超过k
个时,数组中连续1的最大个数
本题的暴力解法为:枚举子数组+记录0的个数,在暴力解法的基础上,对暴力解法进行优化,在枚举的过程中,定义两个指针left
和right
,均从起始位置开始向右移动,当right
位置对应值为0时,计数器zero
加1,代表0出现1次,便于后面与k
进行比较判断,当zero>k
时,说明此时[left, right]
区间为满足要求的区间,接着移动left
以示例数组为例
正向性:当zero>k
时,此时表示0的个数已经到达k
个,接下来需要枚举下一个区间,那么left
就需要向后移动,此时的right
不需要向后移动,因为在区间(left=0)[left, right]
中,0的个数已经到达k
个,如果right
再回到left=1
的位置时,此时的区间(left=1)[left, right]
中依旧包括刚才已经出现的k
个0,导致计数器依旧会在同样的位置达到zero>k
,所以只需要让right
开始位置不需要回退,满足正向性
对于left
指针的移动,为了确保区间[left, right]
中不包括已经判断过的区间,可以让left
移动到上一次满足条件的区间的最后一个元素的位置,[left, right]
区间就避开了包含上一次满足条件的区间,而对于zero
计数器来说,因为left
回到了上一个合法区间的最后一个元素的位置,如果该位置的数值为0,则zero
计数器需要减1,代表这个位置已经翻转过一次,还有k-1
可以翻转,否则不处理
因为满足正向性,所以可以使用滑动窗口算法
-
进窗口:因为是统计0的个数,所以当
right
位置的值为0时,zero
计数器加1,否则不处理 -
判断:当
zero>k
时,此时代表合法区间已经出现,因为k到达上限,所以不可以继续向后移动 -
出窗口:
left
移动到上一个合法区间的最后一个元素的位置,如果为0,则计数器减1 -
更新结果:用
len
存储最长的子数组
参考代码:
cpp
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int len = 0;
int zero = 0;
for (int right = 0, left = 0; right < nums.size(); right++) {
if (nums[right] == 0) {
zero++;
}
while (zero > k) {
if (nums[left++] == 0) {
zero--;
}
}
len = max(len, right - left + 1);
}
return len;
}
};