C++でバイナリ文字列から最小の正しい文字列を求める方法を解説
n ビットの2進数文字列 S があるとします。ただし、余分な先頭のゼロは含まれていないものとします。この文字列 S に対して、次の2種類の操作を実行することができます。
- 隣接する任意の2ビットを入れ替える
- すべての「11」を「1」に置き換える
val(S) を S の10進数表現とします。そして、正しい文字列 A が別の正しい文字列 B よりも小さいとは、val(A) < val(B) が成り立つときであると定義します。この条件下で、最小の正しい文字列を求めるのが本問題の目的です。
具体例
たとえば、入力が S = "1001" の場合を考えてみましょう。このときの出力は「100」となります。次のような一連の操作によって実現できるためです。
"1001" → "1010" → "1100" → "100"
解法の考え方
まず、なぜこのような答えになるのかを考察してみましょう。
「隣接するビットを入れ替える」操作により、実質的に好きな位置へビットを移動させることが可能です。つまり、すべての「0」を先頭の「1」の後ろへ集めることができます。
さらに、「11」を「1」に置き換える操作を繰り返し適用することで、先頭の1つ目の「1」を除くすべての「1」を消去できます。
したがって、最小の正しい文字列は「先頭の1」+「残りの部分に含まれるすべての0」という構成になります。これが数値として最小となる文字列です。
アルゴリズムの手順
この問題を解くために、以下の手順に従います。
- n := 文字列 S の長さとする
- res := 空の文字列を用意し、S[0] を追加する(先頭は必ず「1」)
- i = 1 から n - 1 までループを実行する:
- S[i] が '0' である場合、res に "0" を連結する
- 最後に res を返す
C++による実装例
それでは、理解を深めるために以下の実装例を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
string solve(string S){
int n = S.size();
string res = "";
res += S[0];
for (int i = 1; i < n; i++){
if (S[i] == '0'){
res += "0";
}
}
return res;
}
int main(){
string S = "1001";
cout << solve(S) << endl;
}入力
"1001"
出力
100
このアルゴリズムの計算量は O(n) であり、文字列の長さに比例した効率的な処理が可能です。ビットの入れ替え操作を実際にシミュレーションする必要がなく、0の個数を数えるだけで答えが求まる点がポイントです。
-
C++で文字列から二分木を構築する方法
括弧と整数から構成される文字列が与えられたとき、その文字列から二分木を構築する問題を考えてみましょう。入力文字列全体が一つの二分木を表しており、整数の後に0個、1個、または2組の括弧が続く形式になっています。整数はルート(根)ノードの値を表し、各括弧のペアは同じ構造を持つ子の部分木を含んでいます。問題の例例えば、入力が 4(2(3)(1))(6(5)) のような文字列だった場合、出力は [3,2,1,4,5,6](中順走査・inorder traversal の結果)となります。解決アプローチこの問題を解くために、以下の手順に従います。再帰関数 solve() を定義します。引数として文字列
-
C++で二分木のルートから特定ノードまでの距離を求める方法
二分木が与えられたとき、ルートから特定のノード u までの距離(経路の長さ)を求める問題を考えてみましょう。例として、次のような二分木を想定します。この木において、ルートからノード6までの距離は2、ルートからノード8までの距離は3となります。解決のアプローチこの問題は、再帰的な手法を用いて解くことができます。具体的には、目的のノードを左部分木と右部分木の両方に対して再帰的に探索し、再帰の各段階(レベル)で距離を1ずつ加算していきます。探索の仕組みは以下の通りです。現在のノードがNULLの場合は -1 を返します(ノードが見つからなかったことを示す)。現在のノードの値が目的の値と一致した場合、ま