C++で1ビット反転したときに得られる最長の「1」の連続列を求める方法
問題の概要
ある整数 n が与えられたとき、その中の 1ビットだけを反転(フリップ)することで、最も長く連続する「1」の並びを作ることを考えます。
例として、数値が 13 の場合をみてみましょう。13 の2進表現は 1101 です。ここで 0 を 1 に反転すると 1111 となり、4桁すべてが 1 になります。これが反転によって実現できる最長の「1」の連続列です。
解法のアプローチ
この問題は、与えられた数値の各ビットを下位から順に走査することで効率的に解けます。走査中は次の2つのカウンタを管理します。
- curr(現在の長さ):現在進行中の「1」の連続列の長さ
- prev(直前の長さ):ひとつ前の「1」の連続列の長さ
ビットが 1 であれば curr を増やし、0 を見つけた時点で prev を更新します。具体的には、次のビットが 1 なら prev を curr に設定し、次のビットも 0 なら prev を 0 に戻します。こうすることで、「0 をひとつ挟んで隣接する2つの 1 の連続列」をつなげた長さ(prev + curr + 反転分の1)を常に追跡できます。
なお、すべてのビットが 1 の場合(~number == 0)は反転の余地がないため、int 型の全ビット数をそのまま返します。
C++での実装例
#include<iostream>
using namespace std;
int singleFlipMaxOnes(unsigned number) {
if (~number == 0)
return 8*sizeof(int);
int curr = 0, prev = 0, max_size = 0;
while (number != 0) {
if ((number & 1) == 1)
curr++;
else if ((number & 1) == 0) {
prev = (number & 2) == 0 ? 0 : curr;
curr = 0;
}
max_size = max(prev + curr, max_size);
number >>= 1;
}
return max_size + 1;
}
int main() {
cout << "Maximum length of the sequence with 1s: " << singleFlipMaxOnes(13);
}実行結果
Maximum length of the sequence with 1s: 4
まとめ
このアルゴリズムは数値のビット数に比例した計算量 O(log n) で動作し、余分なメモリを必要としません。ビット演算と2つのカウンタだけで解けるシンプルかつ実用的な手法です。入力 13(2進数 1101)に対しては、0 を 1 に反転して 1111 となるため、最大長 4 が出力されます。
-
【C++】二分木における最長連続シーケンス経路の求め方を解説
問題の概要二分木が与えられたとき、最長の連続シーケンス経路の長さを求める問題を考えます。ここで「経路」とは、ある開始ノードから親子のつながり(親から子へのエッジ)に沿って、木の中の任意のノードまでをたどるノードの列を指します。最長の連続経路は必ず親から子の方向へ進む必要があり、逆方向(子から親)へさかのぼることは認められません。たとえば、次のような二分木が入力として与えられた場合を考えてみましょう。この場合、最長の連続シーケンス経路は 3 → 4 → 5 となるため、出力は 3 になります。アルゴリズムのアプローチこの問題は、木を深さ優先探索(DFS)でたどりながら、連続する値の並びを追跡する
-
C++で整数の1の補数(nビット)を求める方法
1の補数とは本記事では、整数の「1の補数」を求める方法について解説します。C++には補数演算子(~)が用意されており、これを使えば非常に高速に補数を計算できます。ただし、この演算子は32ビット(4バイト)全体に対して補数を求めてしまうため、ここでは「与えられた数値のビット数分だけの補数」を取得する方法を考えます。例として、22という数値を取り上げます。22の2進表現は「10110」であり、その1の補数は「01001」、つまり10進数の9になります。では、この値はどのようにして求めればよいのでしょうか。求め方の手順まず、対象の数値のビット数を求めます。この値をcとします(22の場合、c = 5)