【LeetCode】995. K 连续位的最小翻转次数 Minimum Number of K Consecutive Bit Flips(C++)


题目来源:https://leetcode-cn.com/problems/minimum-number-of-k-consecutive-bit-flips/

题目描述

在仅包含 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 <= A.length <= 30000
1 <= K <= A.length

题目大意

  • 题目意思挺易懂的,在处理连续的子数组之类问题时,应该第一个想到的是滑动窗口做法,在本题中,因为输入数据的只有0和1,最终处理成全1,且子数组是连续的,所以可以用队列实现滑动窗口
  • 当A[i] == 0时,且当前的queue.size() % 2 == 0,表示A[i]是0,且经过了偶数次翻转,结果不变,所以当前需要一次翻转,后将index push进队列
  • 当A[i] != 0时,且当前的queue.size() % 2 != 0,表示A[i]是1,且经过了奇数次翻转,结果为~A[i],所以当前需要一次翻转,后将index push进队列
  • 综上,偶数经过偶数次翻转和奇数经过奇数次翻转结果都为0,判断需要翻转可总结为A[i] == queue.size() % 2

队列实现滑动窗口思想

  • 维护一个队列,如果当前循环的nums[i] == queue.size() % 2时,将该下标i放进队列queue中,之后遍历元素,获取queue的队末尾元素(之前翻转的位置下标index),如果当前的下标i>queue.front(最早需要翻转的元素下标) + K,需要将该下标pop出来,因为此时已经窗口已经超过了K
  • 如果当前元素需要翻转,但是i + K > len,下标i后面的元素不足K个,说明无法进行翻转,那么就表明不可能将当前的数组全部翻转成1
class Solution {
    
    
public:
    int minKBitFlips(vector<int>& A, int K) {
    
    
        queue<int> q;
        int len = A.size();
        int cnt = 0;
        for(int i = 0 ; i < len; ++i){
    
    
            if(q.front() + K <= i && !q.empty()){
    
    
                q.pop();
            }
            if(q.size() % 2 == A[i]){
    
    
                if(i + K > len) return -1;
                q.push(i);
                ++cnt;
            }
        }
        return cnt;
    }
};

复杂度分析

  • 时间复杂度:O(n)。n是数组的长度
  • 空间复杂度:O(K)。原理上队列中的元素最多只能到K

差分思想

  • 此题如果盲目将0翻转成1,将会导致超时,原地模拟翻转数组,这启发着我们不考虑去翻转数字,而是统计每个数字需要翻转的次数。对于一次翻转操作,相当于把子数组中所有数字的翻转次数+1
  • 我们可以维护一个差分数组diff,其中diff[i]表示两个相邻元素A[i - 1]和A[i]的翻转次数的差,对于区间[l,r],将所有元素+1,而利用差分的特性,可以O(1)完成这种操作,只需使diff[l]增加1,diff[r + 1]减少1
  • 遍历到A[i]时,如果A[i] + cnt是偶数,则需要翻转(文章初分析了需要翻转时的条件),翻转区间为[i, i+K-1],对应差分处理是diff[i] += 1,diff[i + K - 1] -= 1
  • 如果i+k>len,则无法执行翻转操作,返回-1

AC代码

class Solution {
    
    
public:
    int minKBitFlips(vector<int>& A, int K) {
    
    
        int len = A.size();
        vector<int> diff(len + 1);
        int ans = 0, cnt = 0;
        for(int i = 0 ; i < len; ++i){
    
    
            cnt += diff[i];
            if((A[i] + cnt) % 2 == 0){
    
    
                if(i + K > len) return -1;
                ++ans;
                ++cnt;
                --diff[i + K];
            }            
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(n)。n为数组的长度
  • 空间复杂度:O(n)。n为数组的长度,因为需要维护一个差分数组diff,长度为n

猜你喜欢

转载自blog.csdn.net/lr_shadow/article/details/113938138
今日推荐