995. K 连续位的最小翻转次数

Difficulty
Hard
Tags
差分
URL
https://leetcode.cn/problems/minimum-number-of-k-consecutive-bit-flips/
Star
给定一个二进制数组 nums 和一个整数 k
k位翻转 就是从 nums 中选择一个长度为 k子数组 ,同时把子数组中的每一个 0 都改成 1 ,把子数组中的每一个 1 都改成 0
返回数组中不存在 0 所需的最小 k位翻转 次数。如果不可能,则返回 -1
子数组 是数组的 连续 部分。
示例 1:
输入:nums = [0,1,0], K = 1 输出:2 解释:先翻转 A[0],然后翻转 A[2]。
示例 2:
输入:nums = [1,1,0], K = 2 输出:-1 解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。
示例 3:
输入:nums = [0,0,0,1,0,1,1,0], K = 3 输出:3 解释: 翻转 A[0],A[1],A[2]: A变成 [1,1,1,1,0,1,1,0] 翻转 A[4],A[5],A[6]: A变成 [1,1,1,1,1,0,0,0] 翻转 A[5],A[6],A[7]: A变成 [1,1,1,1,1,1,1,1]
提示:
  • 1 <= nums.length <= 105
  • 1 <= k <= nums.length

法1 贪心 + 差分

从前往后遍历,每次遇到 0 就从当前位置 开始翻转长度为 k 的子数组 。在翻转的过程中,数组是不断变化的。暴力法当然是每次执行翻转操作的时候真的去操作 长度为 k的区间。
因为 1 翻转 偶数次 还是 1,所以我们可以只记录翻转的次数,然后遍历的时候去判断是否需要翻转。
因为执行翻转是对长度为k的区间进行操作,所以采用差分数组来记录翻转次数,差分的前缀和就是当前位置的真实翻转次数。
class Solution: def minKBitFlips(self, nums: List[int], k: int) -> int: diff = [0 for _ in range(len(nums)+1)] # 差分的前缀和就是值本身 sum_ = diff[0] res = 0 for i in range(0, len(nums)): sum_ += diff[i] # 判断是否还需要翻转 if nums[i] == 1 and sum_ % 2 == 1 or nums[i] == 0 and sum_ % 2 == 0: if i + k - 1 >= len(nums): return -1 sum_ += 1 res += 1 diff[i+k-1+1] -= 1 return res