C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で部分配列のビットごとのOR(Bitwise OR)の異なる結果数を求める方法

問題概要

負でない整数からなる配列 A があるとします。すべての連続する部分配列 B = [A[i], A[i+1], ..., A[j]](i ≤ j)に対して、B に含まれる全要素のビットごとのORを計算し、A[i] | A[i+1] | ... | A[j] という結果を得ます。このとき、得られる可能性のある結果の個数を求めるのが目的です。ただし、同じ結果が複数回現れた場合は、最終的な答えには1回だけカウントします。

例えば、入力が [1,1,2] の場合を考えてみましょう。部分配列は [1]、[1]、[2]、[1,1]、[1,2]、[1,1,2] の6通りあり、それぞれのORの結果は 1、1、2、1、3、3 となります。したがって、異なる結果は「1」「2」「3」の3つとなり、答えは 3 です。

アルゴリズム

この問題を解くために、以下の手順に従います。

  • 2つの集合 ret と curr2 を作成します。

  • i を 0 から配列のサイズまで繰り返します。

    • 新しい集合 curr1 を作成し、A[i] を挿入します。

    • curr2 内の各要素 e について、(e OR A[i]) を curr1 に挿入します。

    • curr1 内の各要素 e について、e を ret に挿入します。

    • curr2 := curr1 と更新します。

  • ret のサイズを返します。

ここで重要なポイントは、「あるインデックス i で終わる部分配列のORの値」の候補は、int型のビット数(32)以下にしかならないということです。OR演算はビットを立てる方向にのみ働くため、部分配列の左端を伸ばしてもORの値は増えるか同じままとなり、異なる値の個数はビット数で頭打ちになります。そのため、curr2 には「直前のインデックスで終わる部分配列のOR値の集合」だけを保持すればよく、全体の計算量は O(n × ビット数) 程度に抑えられます。

実装例

理解を深めるために、以下のC++による実装を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
class Solution {
    public:
    int subarrayBitwiseORs(vector<int>& A) {
        unordered_set <int> ret;
        unordered_set <int> curr2;
        for(int i = 0; i < A.size(); i++){
            unordered_set <int> curr1;
            curr1.insert(A[i]);
            unordered_set<int>::iterator it = curr2.begin();
            while(it != curr2.end()){
                curr1.insert(*it | A[i]);
                it++;
            }
            it = curr1.begin();
            while(it != curr1.end()){
                ret.insert(*it);
                it++;
            }
            curr2 = curr1;
        }
        return ret.size();
    }
};
main(){
    vector<int> v = {1,1,2};
    Solution ob;
    cout << (ob.subarrayBitwiseORs(v));
}

入力

[1,1,2]

出力

3
  1. C++のビットごとのOR(|)演算子とは?仕組みと使い方を解説

    C++におけるビットごとのOR演算子(|)は、第1オペランドの各ビットと、第2オペランドの対応するビットを比較する演算子です。比較した2つのビットのうちどちらか一方でも1であれば、結果の対応するビットは1にセットされます。両方のビットが0である場合にのみ、結果は0となります。なお、このビット包含的OR演算子を使用する際は、両方のオペランドが整数型(int、unsigned shortなど)である必要があります。真理値表ビットごとのORの動作は、以下の真理値表のようにまとめられます。ビットAビットBA | B の結果000011101111サンプルコード#include <iostream

  2. C++のビット演算子(AND・OR・XOR)の使い方を解説

    C++で利用できるビット演算子(ビットごとの演算を行う演算子)には主に3種類あります。ビットごとのAND演算子(&)、ビットごとのOR演算子(|)、そしてビットごとの排他的OR演算子であるXOR(^)です。これらの演算子は、データを2進数のビット列として扱い、対応するビット同士を比較することで新しい値を生成します。フラグ管理やマスク処理など、低レベルなプログラミングで頻繁に活用される重要な演算子です。ビットごとのAND演算子(&)AND演算子(&)は、第1オペランドの各ビットを、第2オペランドの対応するビットと比較します。両方のビットが1の場合のみ、結果の対応するビット