Pythonで文字列の「良い分割」の数を求めるプログラム
ある文字列 s が与えられているとします。s を2つの空でない文字列 p と q に分割し、その連結が元の s と一致し、さらに p と q のそれぞれに含まれる異なる文字の種類数が等しいとき、この分割を「良い分割(good split)」と呼びます。この記事では、s に対して作れる良い分割の数を求める方法を解説します。
問題の例
たとえば、入力が s = "xxzxyx" の場合、出力は 2 になります。分割の仕方は複数ありますが、("xxz", "xyx") や ("xxzx", "yx") のように分割したときだけ、両側の異なる文字数が一致するため「良い分割」となります。
解決のアプローチ
この問題は、文字列を左から順に走査しながら、左右それぞれの異なる文字数を管理することで効率的に解けます。具体的な手順は以下の通りです。
resultを 0 で初期化します。left:左側の文字の出現頻度を記録するための空のマップ(Counter)を用意します。right:sに含まれる全文字の出現頻度をあらかじめカウントしておきます。s内の各文字cについて次を繰り返します。left[c]を +1 し、right[c]を −1 します。right[c]が 0 になった場合は、キーごと削除して文字種としてカウントされないようにします。leftとrightのサイズ(異なる文字の種類数)が一致していれば、resultを +1 します。
- 最後に
resultを返します。
Pythonでの実装例
以下のコードでは、標準ライブラリの collections.Counter を使うことで、出現頻度の管理を簡潔に記述できます。
from collections import Counter
def solve(s):
result = 0
left, right = Counter(), Counter(s)
for c in s:
left[c] += 1
right[c] -= 1
if not right[c]:
del right[c]
if len(left) == len(right):
result += 1
return result
s = "xxzxyx"
print(solve(s))入力
"xxzxyx"
出力
2
計算量について
このアルゴリズムは文字列を一度だけ走査すればよいため、時間計算量は O(n)、各Counterが保持するキー数は高々アルファベット種類数なので空間計算量も O(1)(文字種に依存)と非常に効率的です。単純にすべての分割位置を試して毎回文字種を数え直す O(n²) の方法と比べ、長い文字列でも高速に動作します。
-
Pythonで二分木の「良い」葉ノードペアの数を求めるプログラム
問題の概要 二分木と整数値 d が与えられます。異なる2つの葉ノードからなるペアのうち、両ノード間の最短経路の長さが d 以下であるものを「良いペア(good pair)」と呼びます。この記事では、Pythonを使って木の中に良いペアがいくつ存在するかを求める方法を解説します。 たとえば、次のような二分木を考えてみましょう。 この木に対して d = 4 とした場合、答えは 2 になります。(8, 7) と (5, 6) の2つのペアは経路長がどちらも 2 で d 以下だからです。一方、(7, 5) や (8, 6) などのペアは経路長が 5 になり、d = 4 を超えるため良いペアとして数
-
Pythonでエンコードされたメッセージのデコード方法の総数を求めるプログラム
問題の概要「a」= 1、「b」= 2、…「z」= 26 というアルファベットと数字の対応関係があるとします。このとき、エンコードされたメッセージ(数字列)が与えられれば、そのメッセージをデコードできる方法が何通りあるかを数えるのが本記事のテーマです。例えば、入力が message = 222 の場合、出力は 3 になります。これは次の3通りにデコードできるためです。b・b・b(2, 2, 2)b・v(2, 22)v・b(22, 2)解決のアプローチ:動的計画法(DP)この問題は動的計画法を用いることで効率的に解くことができます。各位置 i までの文字列についてデコード方法の総数を記録し、1文字