加载中...
995-K 连续位的最小翻转次数(Minimum Number of K Consecutive Bit Flips)
发表于:2021-12-03 | 分类: 困难
字数统计: 476 | 阅读时长: 2分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/minimum-number-of-k-consecutive-bit-flips

英文原文

You are given a binary array nums and an integer k.

A k-bit flip is choosing a subarray of length k from nums and simultaneously changing every 0 in the subarray to 1, and every 1 in the subarray to 0.

Return the minimum number of k-bit flips required so that there is no 0 in the array. If it is not possible, return -1.

A subarray is a contiguous part of an array.

 

Example 1:

Input: nums = [0,1,0], k = 1
Output: 2
Explanation: Flip nums[0], then flip nums[2].

Example 2:

Input: nums = [1,1,0], k = 2
Output: -1
Explanation: No matter how we flip subarrays of size 2, we cannot make the array become [1,1,1].

Example 3:

Input: nums = [0,0,0,1,0,1,1,0], k = 3
Output: 3
Explanation: 
Flip nums[0],nums[1],nums[2]: nums becomes [1,1,1,1,0,1,1,0]
Flip nums[4],nums[5],nums[6]: nums becomes [1,1,1,1,1,0,0,0]
Flip nums[5],nums[6],nums[7]: nums becomes [1,1,1,1,1,1,1,1]

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= k <= nums.length

中文题目

在仅包含 01 的数组 A 中,一次 K 位翻转包括选择一个长度为 K 的(连续)子数组,同时将子数组中的每个 0 更改为 1,而每个 1 更改为 0

返回所需的 K 位翻转的最小次数,以便数组没有值为 0 的元素。如果不可能,返回 -1

 

示例 1:

输入:A = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。

示例 2:

输入:A = [1,1,0], K = 2
输出:-1
解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。

示例 3:

输入:A = [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. 1 <= A.length <= 30000
  2. 1 <= K <= A.length

通过代码

高赞题解

各位题友大家好! 今天是 @负雪明烛 坚持日更的第 25 天。今天力扣上的每日一题是「995. K 连续位的最小翻转次数」。

解题思路

题目大意:每次翻转长度为 K 的子数组,求最少的翻转次数使数组中所有的 0 都更改为 1。如果不能实现,则返回 -1.

  • 结论 1:后面区间的翻转,不会影响前面的元素。因此可以使用贪心策略,从左到右遍历,遇到每个 0 都把 它以及后面的 $K-1$ 个元素进行翻转。
  • 结论 2:$A[i]$ 翻转偶数次的结果是 $A[i]$;翻转奇数次的结果是 A[i] ^ 1

方法一:模拟翻转(超时)

一个直观的思路是,从左到右遍历一遍,遇到数字为 0,那么翻转以该数字为起始的 K 个数字。

代码如下:

class Solution(object):
    def minKBitFlips(self, A, K):
        """
        :type A: List[int]
        :type K: int
        :rtype: int
        """
        N = len(A)
        res = 0
        for i in range(N - K + 1):
            if A[i] == 1:
                continue
            for j in range(K):
                A[i + j] ^= 1
            res += 1
        for i in range(N):
            if A[i] == 0:
                return -1
        return res
  • 时间复杂度:$O(N * K + N)$,超时。
  • 空间复杂度:$O(1)$。

方法二:滑动窗口

上面方法超时的主要原因是我们真实地进行了翻转。根据结论二,位置 $i$ 现在的状态,和它前面 $K - 1$ 个元素翻转的次数(奇偶性)有关。

我们使用队列模拟滑动窗口,该滑动窗口的含义是前面 $K - 1$ 个元素中,以哪些位置起始的 子区间 进行了翻转。该滑动窗口从左向右滑动,如果当前位置 $i$ 需要翻转,则把该位置存储到队列中。遍历到新位置 $j (j < i + K)$ 时,队列中元素的个数代表了 $i$ 前面 $K - 1$ 个元素翻转的次数。

  • 当 $A[i]$ 为 0,如果 $i$ 位置翻转了偶数次,那么翻转后仍是 0,当前元素需要翻转;
  • 当 $A[i]$ 为 1,如果 $i$ 位置翻转了奇数次,那么翻转后变成 0,当前元素需要翻转。

综合上面两点,我们得到一个结论,如果 len(que) % 2 == A[i] 时,当前元素需要翻转。

当 $i + K > N$ 时,说明需要翻转大小为 K 的子区间,但是后面剩余的元素不到 K 个了,所以返回 -1。

示例

下面的动图演示了题目给出的示例三 A = [0,0,0,1,0,1,1,0], K = 3 的情况:

995.gif

对应的 PPT 在这:

<995.001.jpeg,995.002.jpeg,995.003.jpeg,995.004.jpeg,995.005.jpeg,995.006.jpeg,995.007.jpeg,995.008.jpeg,995.009.jpeg,995.010.jpeg,995.011.jpeg,995.012.jpeg,995.013.jpeg,995.014.jpeg,995.015.jpeg,995.016.jpeg,995.017.jpeg,995.018.jpeg,995.019.jpeg,995.020.jpeg>

代码

Python ,Java, C++ 三种代码如下:

[]
class Solution(object): def minKBitFlips(self, A, K): """ :type A: List[int] :type K: int :rtype: int """ N = len(A) que = collections.deque() res = 0 for i in range(N): if que and i >= que[0] + K: que.popleft() if len(que) % 2 == A[i]: if i + K > N: return -1 que.append(i) res += 1 return res
[]
class Solution { public int minKBitFlips(int[] A, int K) { int res = 0; Deque<Integer> que = new LinkedList<>(); for (int i = 0; i < A.length; i++) { if (que.size() > 0 && i > que.peek() + K - 1) { que.removeFirst(); } //1.本来是1,翻转奇数次变为0,所以需要再次翻转,放入队列 //2.本来是0,翻转偶数次还是0,所以需要再次翻转,放入队列 if (que.size() % 2 == A[i]) { if (i + K > A.length) return -1; que.add(i); res += 1; } } return res; } }
[]
class Solution { public: int minKBitFlips(vector<int>& A, int K) { int N = A.size(); queue<int> que; int res = 0; for (int i = 0; i < N; ++i) { if (!que.empty() && i >= que.front() + K) { que.pop(); } if (que.size() % 2 == A[i]) { if (i + K > N) { return -1; } que.push(i); res ++; } } return res; } };
  • 时间复杂度:$O(N)$。
  • 空间复杂度:$O(K)$。

刷题心得

今天的题目理解着比较困难,主要是语言很难说明白,其实理解之后没有那么难。


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、模拟面试、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。
祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!

统计信息

通过次数 提交次数 AC比率
21864 40532 53.9%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
灯泡开关 中等
上一篇:
993-二叉树的堂兄弟节点(Cousins in Binary Tree)
下一篇:
996-正方形数组的数目(Number of Squareful Arrays)
本文目录
本文目录