【LeetCode】995。K個連続ビットフリップの最小数(C ++)


件名のソース:https//leetcode-cn.com/problems/minimum-number-of-k-consecutive-bit-flips/

タイトル説明

01のみを含む配列Aでは、Kビットフリップでは、長さKの(連続した)サブ配列を選択し、サブ配列の各0を1に変更し、各1を0に変更します。

配列に値0の要素がないように、必要なKビットフリップの最小数を返しますそれが不可能な場合は、-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であることを意味し、偶数回のフリップの後、結果は変わらないため、現在のニーズインデックスをキューにプッシュします
  • A [i]!= 0で、現在のqueue.size()%2!= 0の場合、A [i]が1であることを意味し、奇数回のフリップの後、結果は〜A [i]になります。したがって、現在必要なのは1回フリップしてから、インデックスをキューにプッシュすることです。
  • 要約すると、偶数回の偶数回の反転と奇数回の奇数回の反転の結果は0です。反転が必要であるという判断は、A [i] == queue.size()%2として要約できます。

キューはスライディングウィンドウのアイデアを実現します

  • 現在のループがnums [i] == queue.size()%2の場合、キューを維持し、添え字iをキューキューに入れてから、要素をトラバースしてキューの終了要素(前の反転位置の下)を取得します。 )インデックス)、現在のインデックスi> queue.front(最初に反転する必要がある要素のインデックス)+ Kの場合、ウィンドウはこの時点ですでに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]は2つの隣接する要素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は配列の長さであり、長さはnです。

おすすめ

転載: blog.csdn.net/lr_shadow/article/details/113938138