Pythonでn番目のバイナリ文字列のk番目のビットを求める方法
問題の概要
2つの正の整数 n と k が与えられたとき、次の規則に従って生成されるバイナリ文字列 Sn の中から、k番目のビットを求めることを考えます。
- S1 = 0
- i > 1 のとき、Si = Si-1 + "1" + reverse(invert(Si-1))
ここで、reverse(x) は文字列 x を逆順に並べ替えた結果を返し、invert(x) は x のすべてのビットを反転(0と1を入れ替え)させた結果を返します。
この規則によって生成される最初の4つの文字列は以下のとおりです。
- S1 = "0"
- S2 = "011"
- S3 = "0111001"
- S4 = "011100110110001"
例として、n = 4、k = 10 が入力された場合を考えてみましょう。S4 = "011100110110001" であるため、10番目のビットは「1」になります(先頭のビットを位置1として数えます)。
解き方のアプローチ
この問題は、以下の手順で解くことができます。
- k が 1 の場合は「0」を返します(S1 は必ず 0 だからです)。
- それ以外の場合は、要素 0 のみを持つ配列 arr と、要素 1 のみを持つ配列 arr2 を用意します。
- arr の長さが k 以上になるまで、次の操作を繰り返します。
- templast := arr のコピー
- temp2last := arr2 のコピー
- arr := templast + [1] + temp2last
- arr2 := templast + [0] + temp2last
- 最後に、arr の (k-1) 番目の要素を返します。
ポイントとなるのは、arr2 が常に arr のビットを反転した文字列を保持しているという点です。つまり、arr は現在の Si に、arr2 は invert(Si) に対応します。この仕組みにより、毎回文字列全体を明示的に反転・逆順にする処理を行わなくても、次の段階の文字列を効率よく構築できます。また、各段階で文字列の長さは「前の長さ × 2 + 1」(1 → 3 → 7 → 15 …)と増えていくため、k 番目のビットが確定するまでの繰り返し回数はごくわずかで済みます。
Pythonでの実装例
def solve(n, k):
if k == 1:
return str(0)
else:
arr = [0]
arr2 = [1]
while k > len(arr):
templast = arr.copy()
temp2last = arr2.copy()
arr = templast + [1] + temp2last
arr2 = templast + [0] + temp2last
return str(arr[k-1])
n = 4
k = 10
print(solve(n, k))
入力
n = 4, k = 10
出力
1
まとめ
本記事では、再帰的な規則で生成されるバイナリ文字列 Sn の k 番目のビットを求めるアルゴリズムを紹介しました。反転版の配列を同時に管理することで、文字列の反転や逆順の操作を省略でき、シンプルかつ効率的な実装が実現できます。同様の「自己相似的な文字列生成」の問題では、このテクニックが応用できるので、ぜひ覚えておきましょう。
-
Pythonで二分木の指定ノードの右隣ノードを見つけるプログラム
二分木が与えられ、さらに特定のノード「u」へのポインタも渡されたとします。このとき、u のすぐ右側に位置するノード(必ず同じ階層に存在する)を見つける必要があります。対象のノードは葉ノードの場合もあれば、内部ノードの場合もあります。 例として、次のような二分木が入力されたとしましょう。 ここで u = 6 とすると、出力は 8 になります。ノード 6 の右隣にはノード 8 が存在するため、値 8 が返されるというわけです。 解決のためのアプローチ この問題は、両端キュー(deque)を使った幅優先探索(BFS)、いわゆるレベル順走査によって解くことができます。手順は以下の通りです。 ルー
-
Pythonで二値グリッドを整列させるための最小スワップ回数を求めるプログラム
問題の概要n × n の二値(0と1のみ)行列を考えます。この行列に対して、「隣接する2つの行を選んで入れ替える」という操作を1ステップとして実行できます。ここで求めたいのは、行列の主対角線より上側にあるすべての要素が 0 になるようにするために必要な最小スワップ回数です。どのように行を入れ替えても条件を満たせない場合は、-1 を返します。たとえば、次のような入力が与えられたとします。010011100この場合、出力は 2 になります。2回の隣接スワップで行を並べ替えれば、主対角線より上の要素をすべて 0 にできるからです。解き方のポイントこの問題を効率よく解く鍵は、各行を「右端にいくつ 0