C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で0と1の個数が等しいバイナリ部分文字列を数える方法

問題概要

文字列 s が与えられたとき、「0 の個数と 1 の個数が等しく、かつすべての 0 とすべての 1 がそれぞれ連続してまとまっている」ような部分文字列の総数を求めます。同じ内容の部分文字列が複数回現れる場合は、出現した回数だけカウントします。

例えば、入力が "11001100" の場合、条件を満たす部分文字列は "1100""10""0011""01""1100""10" の 6 つとなるため、出力は 6 になります。

解法の考え方

この問題は、文字列を一度走査するだけで O(n) の計算量で解くことができます。ポイントは「現在の文字が連続している回数」と「直前まで別の文字が連続していた回数」を比較するというシンプルな発想です。

具体的には、以下の手順で処理を進めます。

  • サイズ 2 の配列 cnt を定義し、0 で初期化します(cnt[0] は '0' の連続数、cnt[1] は '1' の連続数を管理)
  • 答えを格納する変数 res を 0 で初期化します
  • i を 0 から文字列の長さ未満まで 1 ずつ増やしながら、以下を繰り返します
    • num = s[i] - '0' として、現在の文字を数値に変換します
    • i が 0 のとき、または s[i] が直前の文字 s[i-1] と異なるとき(文字のグループが切り替わったとき)、cnt[num] を 0 にリセットします
    • cnt[num] を 1 増やします
    • cnt[num] が反対側の文字の連続数 cnt[1-num] 以下であれば、res を 1 増やします
  • 最後に res を返します

このアルゴリズムが正しく機能するのは、現在位置で終わる有効な部分文字列が存在するのは「現在の文字の連続数が、直前の異なる文字の連続数を超えない場合」と限られるからです。例えば "0011" の場合、最初の '1' の時点では cnt[1]=1 ≤ cnt[0]=2 なので "01" が成立し、次の '1' の時点でも cnt[1]=2 ≤ cnt[0]=2 なので "0011" が成立します。このように各位置での判定が積み重なり、全体の答えが得られます。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
    int countBinarySubstrings(string s) {
        int cnt[2] = { 0 };
        int res = 0;
        for (int i = 0; i < s.length(); ++i) {
            int num = s[i] - '0';
            if (i == 0 || s[i] != s[i - 1])
                cnt[num] = 0;
            ++cnt[num];
            if (cnt[num] <= cnt[1 - num])
                ++res;
        }
        return res;
   }
};
main(){
    Solution ob;
    cout << (ob.countBinarySubstrings("11001100"));
}

入力

"11001100"

出力

6

まとめ

この手法では文字列を一度だけ走査すればよいため、時間計算量は O(n)、追加の記憶領域も定数 O(1) で済みます。すべての部分文字列を実際に生成して検証する代わりに、「連続する同一文字のグループ長を比較する」という発想で効率よく答えを求められるのが最大のポイントです。

  1. C++で二分木の「良いノード」を数えるアルゴリズム

    問題の概要二分木が与えられたとき、木の中のノードXは、ルートからXへの経路上に、Xより大きい値を持つノードが存在しない場合に「良いノード(good node)」と定義されます。この記事では、二分木に含まれる良いノードの数を求める方法を解説します。例えば、次のような二分木が入力として与えられたとします。この場合、出力は 4 となります。色付きで示されたノードが「良いノード」に該当します。解決アプローチこの問題は、深さ優先探索(DFS)を使うことで効率的に解くことができます。具体的な手順は以下の通りです。DFS関数の定義dfs() 関数を定義します。引数としてノード(node)と現在までの最大値(

  2. C++で高さhの平衡二分木(バランス木)の総数を求める方法

    本記事では、二分木の高さHが与えられたとき、その高さを持つ平衡二分木(バランスの取れた二分木)が何通り存在するかをC++で求める方法を解説します。 二分木とは 二分木(バイナリツリー)とは、各ノードが最大2つの子ノード(左の子と右の子)を持つ木構造のデータ構造です。 高さ平衡二分木とは 高さ平衡二分木(height-balanced binary tree)とは、すべてのノードにおいて、左部分木と右部分木の深さの差が0または1しかない二分木として定義されます。つまり、どのノードを見ても、左部分木と右部分木の高さの差は最大で1である必要があります。 次の図は、高さh=3の場合に考えられる高さ平衡