0995. K 连续位的最小翻转次数
大约 3 分钟
0995. K 连续位的最小翻转次数
- 标签:位运算、队列、数组、前缀和、滑动窗口
- 难度:困难
题目链接
题目大意
描述:给定一个仅包含 和 的数组 ,再给定一个整数 。进行一次 位翻转包括选择一个长度为 的(连续)子数组,同时将子数组中的每个 更改为 ,而每个 更改为 。
要求:返回所需的 位翻转的最小次数,以便数组没有值为 的元素。如果不可能,返回 。
说明:
- 子数组:数组的连续部分。
- 。
- 。
示例:
- 示例 1:
输入:nums = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。
- 示例 2:
输入: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:滑动窗口
每次需要翻转的起始位置肯定是遇到第一个元素为 的位置开始反转,如果能够使得整个数组不存在 ,即返回 作为反转次数。
同时我们还可以发现:
- 如果某个元素反转次数为奇数次,元素会由 ,。
- 如果某个元素反转次数为偶数次,元素不会发生变化。
每个第 位置上的元素只会被前面 的元素影响。所以我们只需要知道前面 个元素翻转次数的奇偶性就可以了。
同时如果我们知道了前面 个元素的翻转次数就可以直接修改 了。
我们使用 记录第 个元素之前 个位置总共被反转了多少次,或者 是大小为 的滑动窗口。
- 如果前面第 个元素翻转了奇数次,则如果 ,则 也被翻转成了 ,需要再翻转 次。
- 如果前面第 个元素翻转了偶数次,则如果 ,则 也被翻转成为了 ,需要再翻转 次。
这两句写成判断语句可以写为:if (flip_count + nums[i]) % 2 == 0:
。
因为 ,所以我们可以用 和 以外的数,比如 来标记第 个元素发生了翻转,即 nums[i] = 2
。这样在遍历到第 个元素时,如果有 ,则说明 发生了翻转。同时根据 和 来判断第 位是否需要进行翻转。
整个算法的具体步骤如下:
- 使用 记录最小翻转次数。使用 记录窗口内前 $k - 1 $ 位元素的翻转次数。
- 遍历数组 ,对于第 位元素:
- 如果 ,并且 ,需要缩小窗口,将翻转次数减一。(此时窗口范围为 )。
- 如果 ,则说明 还需要再翻转一次,将 标记为 ,同时更新窗口内翻转次数 和答案最小翻转次数 。
- 遍历完之后,返回 。
思路 1:代码
class Solution:
def minKBitFlips(self, nums: List[int], k: int) -> int:
ans = 0
flip_count = 0
for i in range(len(nums)):
if i - k >= 0 and nums[i - k] == 2:
flip_count -= 1
if (flip_count + nums[i]) % 2 == 0:
if i + k > len(nums):
return -1
nums[i] = 2
flip_count += 1
ans += 1
return ans
思路 1:复杂度分析
- 时间复杂度:,其中 为数组 的长度。
- 空间复杂度:。