C++でバイナリ文字列を交互に並べ替えるための最小スワップ回数を求めるアルゴリズム
問題概要
0と1の個数が等しく、長さが偶数のバイナリ文字列が与えられます。この文字列を「交互文字列(隣り合うどの2文字も同じ文字にならない状態)」に変換するために必要な最小のスワップ回数を求めてください。
例
例えば、str = "11110000" の場合、必要なスワップ回数は 2回 です。2回の入れ替えで "10101010" のような交互文字列に変換できます。
アルゴリズムの考え方
- 文字列の偶数インデックス(i % 2 == 0)と奇数インデックスにそれぞれ存在する「0」の個数を数えます。それぞれ
evenZeroCnt、oddZeroCntとします。 - 同様に、偶数インデックスと奇数インデックスに存在する「1」の個数も数えます。それぞれ
evenOneCnt、oddOneCntとします。 - スワップは必ず「1」と「0」の間で行われます。したがって、交互文字列が「0」で始まる場合に必要なスワップ回数は
min(evenZeroCnt, oddOneCnt)、「1」で始まる場合はmin(evenOneCnt, oddZeroCnt)となります。 - 最終的な答えは、この2つの候補のうち小さい方です。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
int getMinSwaps(string str) {
int oddZeroCnt = 0;
int evenZeroCnt = 0;
int oddOneCnt = 0;
int evenOneCnt = 0;
int n = str.length();
for (int i = 0; i < n; ++i) {
if (i % 2 == 0) {
if (str[i] == '1') {
++evenOneCnt;
} else {
++evenZeroCnt;
}
} else {
if (str[i] == '1') {
++oddOneCnt;
} else {
++oddZeroCnt;
}
}
}
int zeroSwapCnt = min(evenZeroCnt, oddOneCnt);
int oneSwapCnt = min(evenOneCnt, oddZeroCnt);
return min(zeroSwapCnt, oneSwapCnt);
}
int main() {
string str = "11110000";
cout << "Minimum swaps = " << getMinSwaps(str) << endl;
return 0;
}上記のプログラムをコンパイルして実行すると、次の出力が得られます。
出力
Minimum swaps = 2
計算量
- 時間計算量: O(n) — 文字列を一度走査するだけで完了します。
- 空間計算量: O(1) — カウンタ用の変数のみを使用します。
このアプローチのポイントは、実際にスワップを行うことなく、位置ごとの文字の出現回数を集計するだけで答えを導出できる点です。0と1の総数が等しいという前提条件があるため、偶数位置と奇数位置のカウントには美しい対称性が成り立ち、シンプルな比較だけで最小スワップ回数が求まります。
-
C++で文字列を回文にするために必要な最小削除文字数を求める方法
問題の概要長さ n の文字列が与えられたとき、その文字列を回文にするために削除が必要な最小の文字数を求めるのがこの問題の目的です。たとえば、入力文字列が「abcda」の場合、先頭と末尾以外の2文字を削除すれば回文を作ることができます。「b」と「c」を削除すると → 「ada」(回文になります)「c」と「d」を削除すると → 「aba」(回文になります)「b」と「d」を削除すると → 「aca」(回文になります)アルゴリズムの考え方この問題は「最長回文部分列(Longest Palindromic Subsequence: LPS)」の考え方を使うと効率的に解けます。手順は次のとおりです。与えら
-
【C++】配列を互いに素な配列に変換するための最小挿入回数を求める方法
問題の概要 今回は、与えられた配列を互いに素な配列(コプライム配列)に変換するために必要な最小の挿入回数を求める、興味深い問題を取り上げます。互いに素な配列とは、隣り合う任意の2つの要素の最大公約数(GCD)が必ず1になる配列のことです。この記事では、必要な挿入回数に加えて、変換後の配列そのものも出力します。 例として、{5, 10, 20} という配列を考えてみましょう。この配列は隣接要素同士のGCDが5や10となるため、互いに素な配列ではありません。しかし、5と10の間、そして10と20の間にそれぞれ「1」を挿入すれば、{5, 1, 10, 1, 20} となり、すべての隣接ペアのGCD