最大连续1的个数 III

标签: 数组 二分查找 前缀和 滑动窗口

难度: Medium

给定一个二进制数组 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

Submission

运行时间: 54 ms

内存: 16.7 MB

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        zero = j = 0
        for x in nums:
            if x == 0:
                zero += 1
            if zero > k:
                if nums[j] == 0:
                    zero -= 1
                j += 1
        return len(nums) - j

Explain

本题解使用滑动窗口的策略,通过遍历数组维护一个窗口,该窗口可以包含最多 k 个 0 和任意数量的 1。遍历数组 nums 时,如果遇到 0,则增加 zero 计数器。如果 zero 计数器超过 k,表示当前窗口不满足条件,此时需要调整窗口的左边界 j,直到窗口内的 0 的数量不超过 k 为止。最终,窗口的最大宽度即为数组中可以通过翻转最多 k 个 0 而形成的最长连续 1 的长度。

时间复杂度: O(n)

空间复杂度: O(1)

# Definition for the function to find the longest sequence of 1's with flips

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        zero = j = 0  # Initialize zero count and left index of the window
        for x in nums:  # Iterate over each element in nums
            if x == 0:
                zero += 1  # Increment zero count if the current element is 0
            if zero > k:  # If zero count exceeds k, shrink the window from the left
                if nums[j] == 0:
                    zero -= 1  # Decrement zero count if the element at j is 0
                j += 1  # Move the left index of the window to the right
        return len(nums) - j  # The maximum width of the window is the result

Explore

在滑动窗口策略中,当数组中的0的数量(由变量zero计数)超过了k时,需要移动窗口的左边界。这是因为窗口内最多只能包含k个0,以满足题目条件。如果zero计数超过k,就从窗口左端开始移除元素,直到窗口中的0的数量再次等于或小于k。这通常通过增加左边界索引j来实现,如果移除的元素是0,则相应地减少zero计数。

如果数组中所有元素都是0,并且k小于数组长度,算法的输出将是k。这是因为算法可以将最多k个0翻转为1,从而在数组中形成一个由k个连续1组成的序列。由于窗口的长度不能超过数组长度,且最多只能翻转k个0,因此最长的连续1序列的长度即为k。

在调整窗口左边界时,算法会检查左边界索引j所指向的元素。如果这个元素是0,则在移动窗口左边界(即增加j的值)之前,将zero计数器减1。如果元素是1,则直接移动左边界,不对zero计数器做任何调整。这样可以确保zero计数器的减少总是与窗口左端被移除的0相对应。

使用`len(nums) - j`计算方式是因为在遍历数组结束时,j代表了最终确定的、可以满足条件的窗口的左边界的位置。`len(nums) - j`表示从索引j到数组结束的距离,即窗口的大小。这种计算方式假设从j到数组末尾的部分都可以通过翻转不超过k个0来形成连续的1。因此,这种计算方式考虑了窗口内可能的1的数量,并且假设最优情况下,窗口内所有的0都被翻转成了1。