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

【Python】バイナリ文字列に含まれる「1」のみの部分文字列を数えるアルゴリズム

バイナリ文字列 s が与えられたとき、「1」だけで構成される部分文字列(substring)の総数を求める問題を考えてみましょう。答えが非常に大きくなる可能性がある場合は、結果を 109+7 で割った余りを返します。

問題の例

たとえば、入力が s = "100111" の場合を考えます。このとき出力は 7 になります。

なぜなら、「1」のみを含む部分文字列は次の 7 つだからです。

  • 「1」 × 4つ(各位置にある単独の1)
  • 「11」 × 2つ
  • 「111」 × 1つ

解法のアプローチ

この問題は、文字列を左から右へ一度走査するだけで解くことができます。ポイントは、「現在位置で終わる『1』のみの部分文字列の数」を累積していくことです。

具体的には、以下の手順に従います。

  1. カウンタ a = 0count = 0 を初期化します。
  2. i を 0 から文字列の長さ - 1 まで繰り返します。
    • s[i] が "0" の場合:a = 0 にリセットします(連続した「1」が途切れたため)。
    • それ以外の場合:a += 1 として連続カウントを増やし、count += a で合計に加算します。
  3. 最後に count を返します。

仕組みの解説

このアルゴリズムが正しく動作する理由は、連続する「1」が長さ n のブロックを形成するとき、そのブロックから取り出せる「1」のみの部分文字列は n × (n+1) / 2 個になるためです。変数 a は「現在の位置で終わる部分文字列の数」を表しており、これを毎回加算することで全体の総数が自然に求まります。

計算量は時間・空間ともに効率的で、時間計算量は O(n)、追加の空間計算量は O(1) です。

実装例

以下は Python での実装コードです。

def solve(s):
    a = 0
    count = 0
    for i in range(len(s)):
        if s[i] == "0":
            a = 0
        else:
            a += 1
            count += a
    return count

s = "100111"
print(solve(s))

入力

"100111"

出力

7

まとめ

このように、連続する「1」の長さを追跡しながら累積加算を行うことで、線形時間で「1」のみの部分文字列の総数を効率的に求めることができます。大きな入力に対しても高速に動作するため、競技プログラミングなどでも活用できるテクニックです。

  1. Pythonでn個のノードから構成できる二分探索木(BST)の数を求める方法

    問題の概要互いに異なるn個のノードが与えられたとき、それらを二分探索木(BST:Binary Search Tree)として配置する方法が何通りあるかを求めることを考えます。二分探索木には「左部分木には常に親より小さい値が、右部分木には常に親より大きい値が格納される」という重要な性質があります。この問題を解くには、カタラン数(Catalan Number)を利用します。カタラン数 C(n) は、n個の異なるキーから構成できる二分探索木の総数を正確に表すことが知られています。計算式は次のとおりです。$$C(n)=\frac{(2n)!}{(n+1)!\times n!}$$例えば、入力が n =

  2. Pythonで指定されたインデックスに基づいて文字列をシャッフルする方法

    文字列 s とインデックスのリスト ind が与えられ、両者は同じ長さであるとします。文字列 s は、位置 i にある文字が最終的な文字列内の ind[i] の位置へ移動するようにシャッフルされます。このとき、シャッフル後の最終的な文字列を求める必要があります。例えば、入力が s = ktoalak、ind = [0,5,1,6,2,4,3] の場合、出力は kolkata となります。解決手順この問題を解くには、以下の手順に従います。fin_str を s と同じサイズのリストとして作成し、0で初期化するs 内の各インデックス i と各文字 v に対して、次の操作を行うfin_str[ind