C++でソート順のN番目のバイナリ文字列を効率的に求める方法
問題概要
この問題では、正の整数Nが与えられます。記号「a」と「b」のみを使用して生成できる無限の文字列リストを辞書順(辞書式順序)に並べたとき、そのN番目の文字列を見つけることが課題です。
文字列のリストは以下のように並んでいます。
a, b, aa, ab, ba, bb, aaa, aab, aba, …
例で問題を理解する
入力:N = 8 出力:aab
解法アプローチ
最も単純な解決策は、ループを使って文字列を先頭から順にすべて生成し、N番目の文字列を返す方法です。この方法でも正しい結果は得られますが、Nが大きな値になる場合には計算コストが膨大になり、効率的な解とは言えません。
そこで、より短時間で答えを導ける別のアプローチを紹介します。
効率的な解法の一つが、「相対インデックス(relative index)」を利用する方法です。長さLの文字列は2種類の記号から 2L 通り生成できるという事実を活用します。まず、log2(N+1) の切り捨て値から目的の文字列の長さを求め、相対インデックスを次の式で計算します。
相対インデックス = N + 1 − 2floor(log2(N+1))
この相対インデックスを2進数に変換し、各ビットを「0 → a」「1 → b」に対応させることで、目的の文字列を直接構築できます。これにより、全文字列を生成することなく O(log N) で答えを求められます。
実装例
以下は、この解法の動作を示すC++プログラムです。
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
string findBinString(ll n){
ll len = (int)log2(n + 1);
int ri = n + 1 - pow(2, len);
ll i = 0;
string binString = "";
for (i = 0; i < len; i++) {
binString += 'a';
}
i = 0;
while (ri > 0) {
if (ri % 2 == 1)
binString[i] = 'b';
ri /= 2;
i++;
}
reverse(binString.begin(), binString.end());
return binString;
}
int main(){
ll n = 245;
cout<<"The "<<n<<"-th binary string in sorted order is "<<findBinString(n);
return 0;
}
出力
The 245-th binary string in sorted order is bbbabba
-
C++で二分木の先行順走査(プレオーダー)におけるN番目のノードを求める方法
この記事では、二分木と整数 N が与えられたときに、先行順走査(プレオーダートラバーサル)における N 番目のノードを見つける方法を解説します。まず用語を整理しましょう。二分木とは、各ノードが最大で2つの子ノードを持つことができる特別な木構造のことです。また、走査(トラバーサル)とは、木に含まれるすべてのノードを順番に訪問し、必要に応じてその値を出力する処理のことを指します。先行順走査は「根 → 左部分木 → 右部分木」の順序でノードを訪問する方式です。具体例で問題を理解しよう入力N = 6以下のような二分木を考えます。出力6解説木の先行順走査の結果:1, 2, 4, 5, 3, 6, 7この
-
C++で二分木の垂直順走査におけるK番目のノードを求める方法
二分木と値Kが与えられたとき、垂直順走査(Vertical Order Traversal)におけるK番目のノードを出力するのが課題です。該当するノードが存在しない場合は-1を返します。例として、次のような二分木を考えてみましょう。この二分木を垂直順に走査すると、結果は以下のようになります。4 2 1 5 6 3 8 7 9つまり、K = 3 の場合、答えは 1 となります。アプローチの解説考え方は非常にシンプルです。まず垂直順走査を実行し、走査中の現在のノードがK番目のノードかどうかを順番に確認していきます。K番目に到達した時点で、そのノードの値を返します。垂直順走査では、各ノードに水平距離