Leetcode 995:K 连续位的最小翻转次数(超详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:coordinate@live.com https://blog.csdn.net/qq_17550379/article/details/87685137

在仅包含 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

解题思路

实际上这个问题非常简单,我们只要保证每次对最左边的0的位置i开始的区间[i,i+k]进行反转即可,因为只有这样我们才可以保证我们最后可以将数组变成全部为1的形式。

借此思路,我们不难想到如下的解法

class Solution:
    def minKBitFlips(self, A: 'List[int]', K: 'int') -> 'int':
        res, n = 0, len(A)
        for i, a in enumerate(A):
            if a == 0:
                if n - i < K:
                    return -1
                for j in range(i, i + K):
                    A[j] ^= 1
                res += 1
        return res

但是上面的解法超时了。也就是当K比较大的时候,我们做了过多的反转操作,所以我们这里不难想到通过添加记忆化的方式去优化上面的代码。

我们通过建立一个queue去存放我们需要反转的起始位置,当前位置是不是需要反转,我们就需要确定queue的长度,通过queue的长度我们可以知道,在遍历到这个位置之前我们总共反转了多少次,所以我们只需要判断当前的值a加上queue的长度,判断其是不是偶数即可。这里我们就需要更新我们的queue,也就是当我们此时要访问的元素的位置恰好是队首元素queue[0]加上K(也就是当前元素位置刚出queue[0]的反转区间),此时我们就需要将其出队。

class Solution:
    def minKBitFlips(self, A: 'List[int]', K: 'int') -> 'int':
        q, res, n = list(), 0, len(A)
        for i, a in enumerate(A):
            if q and q[0] + K == i:
                q.pop(0)
                
            if (len(q) + a) & 1 == 0:
                if i > n - K:
                    return -1
                q.append(i)
                res += 1
                
        return res

一个更快的写法

class Solution:
    def minKBitFlips(self, A: 'List[int]', K: 'int') -> 'int':
        cur = res = 0
        for i in range(len(A)):
            if i >= K:
                cur -= A[i - K] // 2
            if (cur + A[i]) & 1 == 0:
                if i + K > len(A):
                    return -1
                A[i] += 2
                cur, res = cur + 1, res + 1
        return res

我们通过cur来表示我们当前遍历到的元素需要反转的次数,通过A[i]+=2来描述我们之前元素的反转次数,这里有一个trick,为什么不是+1?因为要避免原来的数是1的情况,那么相应的,我们就要通过//2的方式计算实际的反转次数。其他的地方都和之前算法思想一致。

reference:

https://leetcode.com/problems/minimum-number-of-k-consecutive-bit-flips/discuss/238609/JavaC%2B%2BPython-One-Pass-and-O(1)-Space

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

展开阅读全文

没有更多推荐了,返回首页