C++で解く「接尾辞内の異なる整数の個数」クエリ問題:効率的な前計算アルゴリズム
この記事では、配列 arr[](要素数 n の整数値)と、それぞれ整数 k を含む Q 個のクエリが与えられたとき、「接尾辞内に存在する異なる整数の個数」を効率的に求める C++ プログラムを紹介します。
問題の概要
各クエリに対して、インデックス k から n までの範囲、すなわち arr[k] から arr[n] までに含まれる一意な要素の数を求める必要があります。
※ 配列は 1 インデックス(1 起点)とします。
入出力例
入力
arr[] = {5, 1, 2, 1, 6, 5}, n = 6, Q = 3, query = {1, 3, 4}
出力
4 4 3
解説
クエリ 1: k = 1 の場合 → arr[1]〜arr[6] の範囲 {5, 1, 2, 1, 6, 5} の一意な要素は {5, 1, 2, 6} の 4 個
クエリ 2: k = 3 の場合 → arr[3]〜arr[6] の範囲 {2, 1, 6, 5} の一意な要素は 4 個
クエリ 3: k = 4 の場合 → arr[4]〜arr[6] の範囲 {1, 6, 5} の一意な要素は 3 個
シンプルな解法(ナイーブなアプローチ)
最も単純な方法は、各クエリごとにインデックス k から n まで順番に走査し、その範囲内の一意な要素を数えて返すことです。しかし、この方法ではクエリごとに O(n) の計算が必要となり、Q 個のクエリがある場合は全体で O(Q × n) の時間計算量となり、非効率です。
効率的な解法(前計算によるアプローチ)
より効率的な解法は、事前に計算されたデータ構造を利用することです。具体的には、インデックス (n-1) から 0 に向かって配列を逆順に走査しながら、unordered_set を使って重複する要素の追加を防ぎ、各位置における一意な要素の累積個数を補助配列に記録します。
この前計算により、各クエリには配列参照 1 回で答えられるため、全体の処理が大幅に高速化されます。
C++ 実装例
#include <bits/stdc++.h>
using namespace std;
void solveQueries_DistInt(int n, int arr[], int Q, int queries[]) {
unordered_set<int> uniqueInts;
int distIntCount[n + 1];
// 後ろから前へ走査し、各位置での一意な要素数を記録
for (int i = n - 1; i >= 0; i--) {
uniqueInts.insert(arr[i]);
distIntCount[i + 1] = uniqueInts.size();
}
// 各クエリに即座に回答
for (int i = 0; i < Q; i++)
cout << "For Query " << (i + 1)
<< ": the number of distinct integers in Suffix is "
<< distIntCount[queries[i]] << endl;
}
int main() {
int n = 6, Q = 3;
int arr[n] = {5, 1, 2, 1, 6, 5};
int queries[Q] = {1, 3, 4};
solveQueries_DistInt(n, arr, Q, queries);
return 0;
}
実行結果
For Query 1: the number of distinct integers in Suffix is 4 For Query 2: the number of distinct integers in Suffix is 4 For Query 3: the number of distinct integers in Suffix is 3
計算量のまとめ
このアプローチでは、前計算に O(n)、各クエリへの回答に O(1) かかるため、全体の時間計算量は O(n + Q) となります。クエリの数が多い場合でも高速に動作するのが大きな利点です。重複排除にはハッシュベースの unordered_set を使用しているため、平均的に挿入操作も高速です。
-
C++で10進数を16進数に変換するプログラムの作り方
10進数の数値が入力として与えられたとき、その数値を16進数に変換するのが本記事の目的です。 コンピュータの世界では、16進数は基数16で表現され、10進数は基数10で表現されます。10進数は0〜9の値を使って表されるのに対し、16進数は0〜15の数字を持ちます。そのうち10は「A」、11は「B」、12は「C」、13は「D」、14は「E」、15は「F」として表されます。 10進数から16進数への変換手順 10進数を16進数に変換するには、以下の手順に従います。 まず、与えられた数値を変換先の基数で割ります。たとえば、6789を16進数に変換する場合、基数である16で6789を割り、商を求め
-
C++で10進数を2進数に変換するプログラムの書き方
コンピューターの内部では、すべてのデータが2進数(基数2)として扱われています。一方、私たちが日常的に使う10進数は「0〜9」の数字を組み合わせた基数10の記数法です。この記事では、C++を使って入力された10進数を2進数へ変換するプログラムの考え方と実装方法を解説します。10進数から2進数への変換手順10進数を2進数に変換する基本的な方法は、「2で割った余りを順番に記録していく」ものです。具体的には次の手順で行います。まず、変換したい数値を基数である2で割り、商と余りを求めます。余りが0であればその桁は「0」、1であれば「1」として記録します。続いて、得られた商をさらに2で割り、同じように余