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

K 连续位的最小翻转次数

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

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

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

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

这一题的首先思路和贪心思想是很相似的,第一想到的点就是从最左边开始遍历整个数组,如果碰到0就可以翻转之后的K位数字,如果到了[ len-K, len] 的范围内还碰到有0的话,就判断无法满足条件。
但是如果用暴力的方法来写的话时间复杂度是O(nK)而造成超时,所以要用一种巧妙的方法来记录某一位到底反转了几次。
首先对于某一位上的数字,只有0,1两种可能性。所以只有数字反转了奇数次才是有意义的。
那么我们知道题目要求一次是翻转窗口K大小的子数组。我们可以用 差 分 数 组 差分数组 的方式来记录当前数字的翻转次数。

我们构造差分数组 reverse[N+1] ,并且用ans来代表总共进行翻转操作的次数,用CNT代表当前数字翻转的次数。差分的意思是 reverse[i] 代表了原数组A[i-1] 和 A[i] 两位数字的翻转次数之差。换句话说,如果我们在第 p位上检测到了0,那么对于 A[p:p+K]的范围内的数字也全部会进行翻转,但是在这个范围内的数字之间的翻转次数是相同的,在这个情况下是 A[p] 这个数字比 A[p-1] 多翻转了一次, A[p+K] 比 A[p+K-1] 少翻转了一次。所以我们需要在差分矩阵 reverse[p+K] 减少1; reverse[p] 增加1 (这个操作是通过CNT记录的)。
这样我们通过CNT和reverse对应位置上的累加即可求出A 每一位数字的翻转次数。
遍历到 A[i] 时,若A[i]+CNT 是偶数,则代表这里需要进行翻转,则翻转A[i:i+K-1],在这里直接将CNT++, reverse[i+K]-- 即可。
注意到若 i+K>n 则无法执行翻转操作,此时应返回 -1−1。

我们来看第一个例子:
A = [0,1,0] K=2
首先构造reverse = [0,0,0,0] CNT=0 ans=0
对于 i=0 ,首先判断A[i] + CNT,发现是0,则需要翻转A[0], A[1]。于是 当前的翻转次数CNT=1, ans=1。在这里CNT代表的是第0~1位的数字翻转了1次,但是第2位则不需要翻转,所以将差分矩阵reverse[2] – 为[0,0,-1,0]

对于i=1, 首先CNT+=reverse[i] = 1,代表当前数字已经被翻转了一次。再判断A[i]+CNT,发现为0,因为本来第二个数字是1,然后又翻转了一次,所以这里就变成0了。于是需要翻转1,2位,当前翻转次数位CNT=2, ans=2。更新差分矩阵reverse[3]-- ,为 [0,0,-1,-1]

对于i=2, 首先更新CNT + reverse[i] = 1,代表第二位也翻转了一次,再判断A[i]+CNT 发现为1,是奇数,就代表这里已经是1了,不需要翻转。到此判断结束,返回2。

这个题目感觉有点难懂,但是理解差分数组之后确实会很通顺的做出来。
C++ 代码如下

class Solution {
public:
    int minKBitFlips(vector<int>& A, int K) {
        int N = A.size();
        int count = 0;
        vector<int> rev(N+1);
        int cur =0;
        for (int i=0;i<N;i++)
        {
            cur += rev[i];
            if ((A[i]+cur)%2==0)
            {
                if (i+K>N) return -1;
                cur++;
                count++;
                rev[K+i]--;
            }
        }
        return count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值