995. K 连续位的最小翻转次数(滑动窗口+队列辅助)

在仅包含 0 和 1 的数组 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 <= A.length <= 30000
1 <= K <= A.length

分析:
  • 本题采用暴力破解超时,参考了别人的思路后,问题在于如何避免暴力方法中内层循环不停的对i后的K个值不停的翻转。

  • 观察题目发现数组A中只有0,1两种情况,目的是想全部置为1,那么对于0,要翻转奇数次,对于1要翻转偶数次才能达到目的,而每一个当前元素要不要翻转,一方面要看自己是0还是1,另一方面要看该元素前面的元素翻转了多少次。

  • 由此可以看出,如果我们能有一个数据结构记录那些需要翻转的元素信息,就无需对于A[i]元素进行重复的0->1,1->0的操作,在这里我们利用队列先进先出的特性来伴随着数组A进行从左到右的遍历。而队列的实时长度也就反应了当前元素已经被前面的元素的翻转窗口影响(翻转)了多少次,代码及注释如下:

class Solution {
public:
    int minKBitFlips(vector<int>& A, int K) {
        // 借助队列,其中的每一个元素的值表示从A中的哪个位置开始进行一次长度为K的翻转
        queue<int> q;
        int i = 0;
        int reverseTimes = 0;
        while(i < A.size()){
            // 遇到i >= q.front() + K,说明已经走完队列的第一个元素翻转影响的窗口
            // 队首出队,对于当前的i已经不受q.pop的影响了
            if(q.size() > 0 && i >= q.front() + K) q.pop();
            // 想象上来去找第一个需要翻转的0时,q.size与A[i]满足的数量关系
            // 对于遍历到的每一个元素,q.size()表示当前元素已经被翻转了q.size()次
            // 利用了对于0应该翻转奇数次,对于1应该翻转偶数次
            if(q.size() % 2 == A[i]){
                if(i + K > A.size()) return -1;
                q.push(i); // 存的是元素的下标,可以用来记录遍历到的位置
                ++reverseTimes;
            }
            ++i;
        }
        return reverseTimes;
    }
};

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值