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
-
C++のビットごとのOR(|)演算子とは?仕組みと使い方を解説
C++におけるビットごとのOR演算子(|)は、第1オペランドの各ビットと、第2オペランドの対応するビットを比較する演算子です。比較した2つのビットのうちどちらか一方でも1であれば、結果の対応するビットは1にセットされます。両方のビットが0である場合にのみ、結果は0となります。なお、このビット包含的OR演算子を使用する際は、両方のオペランドが整数型(int、unsigned shortなど)である必要があります。真理値表ビットごとのORの動作は、以下の真理値表のようにまとめられます。ビットAビットBA | B の結果000011101111サンプルコード#include <iostream
-
C++のビット演算子(AND・OR・XOR)の使い方を解説
C++で利用できるビット演算子(ビットごとの演算を行う演算子)には主に3種類あります。ビットごとのAND演算子(&)、ビットごとのOR演算子(|)、そしてビットごとの排他的OR演算子であるXOR(^)です。これらの演算子は、データを2進数のビット列として扱い、対応するビット同士を比較することで新しい値を生成します。フラグ管理やマスク処理など、低レベルなプログラミングで頻繁に活用される重要な演算子です。ビットごとのAND演算子(&)AND演算子(&)は、第1オペランドの各ビットを、第2オペランドの対応するビットと比較します。両方のビットが1の場合のみ、結果の対応するビット