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

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    int minKBitFlips(vector<int>& nums, int k) {

    }
};

题目解析

题意:给定一个只有0和1的数组,有给定一个数字k,每次可以翻转任意连续的k个位置的数字,至少需要多少步可以将0全部变成1。如果不能实现,则返回-1

分析:

  • 对于每个位置,只有初始状态和总共被反转了多少次决定了自己最终的状态
  • 每个长度为k的区间,最多只会被反转一次,因为两次反转后对最终结果没有影响

得到如下结论:

  • 结论一:后面区间的反转,不会影响前面的元素。因此可以使用贪心,从左到右遍历,每次遇到0时就把它和它后面的k-1个元素进行翻转
  • 结论二: A [ i ] A[i] A[i]翻转偶数次的结果是 A [ i ] A[i] A[i],翻转奇数次的结果是A[i] ^ 1

模拟翻转(超时)

一个直观的思路是,从前往后遍历数组,如果遇到一个 0,我们将当前位置开始的长度为 k 区间的区间反转。如果遇到0时,剩下的区间长度不足k说明我们没有办法完成反转。

class Solution {
public:
    // 1 <= nums.length <= 10^5
    // 1 <= k <= nums.length
    int minKBitFlips(vector<int>& nums, int k) {
        int step = 0;
        int N = nums.size();
        for (int i = 0; i < N; ++i) {
            if(nums[i] == 0){
                if(i + k > N){
                    return -1;
                }
                for (int j = 0; j < k; ++j) {
                    nums[i + j] ^= 1;
                }
                step++;
            }
        }
        //
        return step;
    }
};
  • 时间复杂度: O(N * K + N),超时
  • 空间复杂度: O(1)

滑动窗口

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

我们使用队列模拟滑动窗口,该滑动窗口的含义是前面 k − 1 k - 1 k1个元素中,以哪些位置起始的子区间进行了翻转。

该滑动窗口从左到右滑动,如果当前位置 i i i需要翻转,则把该位置存储到队列中。遍历到新位置j ( j < i + K ) (j < i + K) (j<i+K)时,队列中元素的个数代表了i被前面k-1个元素翻转的次数。

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

即,当que.size() % 2 == A[i]时,当前元素需要翻转。

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

class Solution {
public:
    int minKBitFlips(vector<int>& nums, int k) {
        int N = nums.size();
        std::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 == nums[i]){
                if(i + k > N){
                    return  -1;
                }
                que.push(i);
                res++;
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值