LeetCode 1004. 最大连续1的个数 III
题目描述

题意分析
给定只包含零和一的数组,最多可以把
k个零改成一,求最终能够获得的最长连续一的长度。可以少用翻转次数,不要求恰好使用k次,也不能通过删除元素把不相邻的位置连接起来。对任意一段连续区间,要把它全部变成一,恰好需要翻转其中的所有零。因此问题等价于:寻找包含至多
k个零的最长连续子数组。只需要统计零,不必真的修改数组。
解法:滑动窗口统计零的数量
核心思路
[!blue]
用闭区间
[left, right]表示当前窗口,zeroCount表示其中零的数量。右端每次向右加入一个元素,如果它是零,就把计数加一。只要zeroCount <= k,这段区间就能在预算内全部变成一。若零超过预算,就从左端依次移出元素,直到重新合法。移出一只缩短窗口,移出零才会减少所需翻转次数,因此收缩可能需要跨过多个一,不能保证只移动一次就够。
为什么不需要让左端回退?一个左端位置一旦因为零太多而被排除,右端继续扩大时,包含这个旧左端的区间只会拥有更多或相同数量的零,不可能重新变合法。反过来,收缩只在超额时进行,恢复合法后立刻停止,留下的是当前右端对应的最长合法窗口。
每个右端都检查一次这样得到的最大可用长度,就覆盖了全局最优答案的结束位置。必须先恢复合法,再用
right - left + 1更新答案,才能保证记录的长度确实能由至多k次翻转得到。
k = 0时,遇到零会收缩到它之后,窗口可以暂时为空,长度为零;全数组的零不超过预算时,左端始终不动,最终长度就是整个数组长度。这些边界都不需要额外分支。
解题步骤
- 初始化
left = 0、zeroCount = 0、ans = 0。- 枚举右端
right,把新加入的零计入zeroCount。- 当
zeroCount > k时,检查将要移出的nums[left];若为零,计数减一,然后推进左端。- 窗口合法后,用
right - left + 1更新最大长度。- 处理完所有右端后返回答案。
代码实现
class Solution {
public int longestOnes(int[] nums, int k) {
int left = 0;
int zeroCount = 0;
int ans = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] == 0) {
zeroCount++;
}
// 窗口中的零就是需要翻转的数量,超额时持续缩左边直到合法。
while (zeroCount > k) {
if (nums[left] == 0) {
zeroCount--;
}
left++;
}
ans = Math.max(ans, right - left + 1);
}
return ans;
}
}
func longestOnes(nums []int, k int) int {
left := 0
zeroCount := 0
ans := 0
for right := 0; right < len(nums); right++ {
if nums[right] == 0 {
zeroCount++
}
// 窗口中的零就是需要翻转的数量,超额时持续缩左边直到合法。
for zeroCount > k {
if nums[left] == 0 {
zeroCount--
}
left++
}
if right-left+1 > ans {
ans = right - left + 1
}
}
return ans
}
复杂度分析
- 时间复杂度:$O(n)$。右端完整扫描数组一次,左端也只向右移动,最多经过每个位置一次;内层收缩的总次数为线性数量。
- 空间复杂度:$O(1)$,只维护两个边界、零计数和答案,不修改或复制输入数组。
关键点总结
[!green]
- 区间内的零数就是变成全一所需的翻转次数,把操作问题变成窗口约束。
- 已排除的左端不会随着右端扩展重新变合法,所以两个指针都可以单向前进。
- 零数等于预算时窗口仍合法,只在超过预算时收缩,恢复后才更新答案。
易错点总结
[!yellow]
- 移出一时也减少
zeroCount,会低估窗口的真实翻转成本;只有移出零才减一。- 收缩条件写成
zeroCount >= k,会把恰好用完预算的合法区间也删除。- 只用一次条件判断移动左端,可能尚未移出任何零,窗口仍非法;当前写法要持续收缩到合法。
- 在收缩前更新答案,可能把超过预算的区间长度记录为可行结果。
- 窗口包含两端,长度为
right - left + 1;每轮重置左端或重新统计整段会破坏线性复杂度。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 487. 最大连续1的个数 II | 中等 | 把最多翻一个0推广为最多k个0,窗口零计数预算相同。 |
| 1493. 删掉一个元素以后全为 1 的最长子数组 | 中等 | 原题必须删除一个位置,本题允许翻转至多k位,不能把全1数组的边界混用。 |
| 424. 替换后的最长重复字符 | 中等 | 维护窗口内可消耗的修改预算;本题预算为窗口内零的个数,该题预算为窗口长度减最大字符频次。 |
| 1208. 尽可能使字符串相等 | 中等 | 维护窗口内可消耗的修改预算;本题预算为窗口内零的个数,该题预算为对应字符差值之和。 |
| 2024. 考试的最大困扰度 | 中等 | 维护窗口内可消耗的修改预算;本题预算为窗口内零的个数,该题分别计算统一为两种答案时的窗口。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!