LeetCode探索(53):1004-最大连续1的个数III

126 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

题目

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 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 就是 1
  • 0 <= 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)。

参考