一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
题目
给定一个二进制数组 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。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= nums.length <= 105^nums[i]不是0就是10 <= k <= nums.length
思考
本题难度中等。题目要求的是返回数组中连续1的最大个数。题目中给定的是一个二进制数组 nums 和一个整数 k,我们可以翻转数组中的最多 k 个 0。
首先,如果我们能找到某个区间[left, right],其中包含不超过 k 个 0,那么函数返回值就是right - left + 1。
其次,要想快速判断一个区间内 0 的个数,我们可以考虑将数组 A 中的 0 变成 1,1 变成 0,求出前缀和数组P。那么 [left, right] 中包含不超过 k 个 1,当且仅当前缀和之差 P[right] − P[left−1] ≤ k。
至此,本题就不难解决了。
解答
方法一:滑动窗口+前缀和
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var longestOnes = function(nums, k) {
const n = nums.length
let left = 0, lsum = 0, rsum = 0
let ans = 0
// 遍历数组nums
for (let right = 0; right < n; ++right) {
// 区间[0, right]的前缀和rsum
rsum += 1 - nums[right]
// 区间[0, left]的前缀和lsum
while (lsum < rsum - k) {
lsum += 1 - nums[left]
++left
}
ans = Math.max(ans, right - left + 1)
}
return ans
}
// 执行用时:76 ms, 在所有 JavaScript 提交中击败了53.34%的用户
// 内存消耗:45.4 MB, 在所有 JavaScript 提交中击败了62.50%的用户
// 通过测试用例:52 / 52
复杂度分析
- 时间复杂度:O(n),其中 n 是数组 A 的长度。
- 空间复杂度:O(1)。