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

C++で整数ストリームから指定した整数との最大XORを求める方法

この問題では、以下のいずれかのタイプに該当するQ個のクエリが与えられます。

タイプ1 − 挿入 (1, i):値iを持つ要素をデータ構造に追加します。

タイプ2 − findXOR (2, i):データ構造内のすべての要素と要素iとのXORの最大値を求めます。

データ構造には、初期状態として要素0のみが含まれているものとします。

問題を理解するための例を見てみましょう。

入力

Queries: (1, 9), (1, 3), (1, 7), (2, 8), (1, 5), (2, 12)

出力

15 15

説明

各クエリを順に処理すると、
(1, 9) => データ構造 => {9}
(1, 3) => データ構造 => {9, 3}
(1, 7) => データ構造 => {9, 3, 7}
(2, 8) => 最大XOR(_, 8) = 15(XOR(7, 8))
(1, 5) => データ構造 => {9, 3, 7, 5}
(2, 12) => 最大XOR(_, 12) = 15(XOR(3, 12))

解法アプローチ

この問題は、トライ(trie)と呼ばれる特殊な探索木データ構造を使うことで効率的に解くことができます。ここで使うトライは、各ノードが2つの子ノードを持ち、数値を2進数のビット列として格納するものです。

具体的な手順は次のとおりです。

  • タイプ1のクエリが来た場合は、その数値の2進表現(32ビット分)を上位ビットから順にトライへ挿入します。
  • タイプ2のクエリが来た場合は、与えられた値の各ビットに対応するパスをトライ上で探索します。このとき、可能な限り「反対のビット」の子ノードを選ぶことで、その桁のXORを1にでき、結果として最大XORが得られます。

この手法により、挿入も検索も1クエリあたりO(32)、すなわちビット長に比例した時間で処理できます。

トライについてさらに詳しく知りたい方は、「トライデータ構造」の解説記事をご参照ください。

ソリューションの動作を示すプログラム:

#include<bits/stdc++.h>
using namespace std;
struct Trie {
    Trie* children[2];
    bool isLeaf;
};
bool check(int N, int i) {
    return (bool)(N & (1<<i));
}
Trie* newNode() {
    Trie* temp = new Trie;
    temp->isLeaf = false;
    temp->children[0] = NULL;
    temp->children[1] = NULL;
    return temp;
}
void insertVal(Trie* root, int x) {
    Trie* val = root;
    for (int i = 31; i >= 0; i--) {
        int f = check(x, i);
        if (! val->children[f])
            val->children[f] = newNode();
        val = val->children[f];
    }
    val->isLeaf = true;
}
int solveQueryType2(Trie *root, int x){
    Trie* val = root;
    int ans = 0;
    for (int i = 31; i >= 0; i--) {
        int f = check(x, i);
        if ((val->children[f ^ 1])){
            ans = ans + (1 << i);
            val = val->children[f ^ 1];
        }
        else
        val = val->children[f];
    }
    return ans;
}
void solveQueryType1(Trie *root, int x){
    insertVal(root, x);
}
int main(){
    int Q = 6;
    int query[Q][2] = {{1, 9}, {1, 3}, {1, 7}, {2, 8}, {1, 5}, {2, 12}};
    Trie* root = newNode();
    for(int i = 0; i < Q; i++){
        if(query[i][0] == 1 ){
            solveQueryType1(root, query[i][1]);
            cout<<"Value inserted to the data Structure. value = "<<query[i][1]<<endl;
        }
        if(query[i][0] == 2){
            cout<<"The maximum XOR with "<<query[i][1]<<" is "<<solveQueryType2(root, query[i][1])<<endl;
        }
    }
    return 0;
}

出力

Value inserted to the data Structure. value = 9
Value inserted to the data Structure. value = 3
Value inserted to the data Structure. value = 7
The maximum XOR with 8 is 15
Value inserted to the data Structure. value = 5
The maximum XOR with 12 is 15
  1. C++で指定された値に最も近いk個の要素を検索する方法

    いくつかの要素を含む配列 A があるとします。ここに、値 X と整数 k も与えられます。この課題は、配列 A の中から X に最も近い k 個の要素を見つけることです。なお、X が配列内に存在する場合は、その要素自体は出力に含めません。 例として、A = [12, 16, 22, 30, 35, 39, 42, 45, 48, 50, 53, 55, 56]、X = 35、k = 4 とすると、出力は「30, 39, 42, 45」になります。 解法の考え方:二分探索を活用する この問題を効率的に解くには、二分探索(バイナリサーチ)の手法を利用します。二分探索によって「クロスオーバーポイ

  2. C++で積がPとなるN個の整数の最大GCDを求める方法

    2つの整数 N と P が与えられているとします。P は N 個の未知の整数の積であり、そのときそれらの整数の最大公約数(GCD)としてあり得る最大値を求めるのが課題です。 例として、N = 3、P = 24 の場合を考えてみましょう。3つの整数の組み合わせとしては {1, 1, 24}、{1, 2, 12}、{1, 3, 8}、{1, 4, 6}、{2, 2, 6}、{2, 3, 4} などが考えられます。それぞれのGCDは 1, 1, 1, 1, 2, 1 となるため、この場合の答えは 2 です。 解法のアプローチ まず P のすべての素因数を求め、ハッシュマップに格納します。各素因数が