Pythonで文字列とその接尾辞との類似度の合計を求めるプログラム
問題の概要
文字列 s が与えられたとき、s とそのすべての接尾辞(末尾から1文字ずつ短くした部分文字列)との「類似度」の合計を求めます。ここで2つの文字列の類似度とは、両方の文字列に共通する最長の接頭辞(先頭からの一致部分)の長さのことです。
例として、入力が s = "pqpqpp" の場合を考えてみましょう。この文字列の接尾辞は次の6つです。
- "pqpqpp"(元の文字列そのもの)
- "qpqpp"
- "pqpp"
- "qpp"
- "pp"
- "p"
それぞれと元の文字列 "pqpqpp" との類似度は、順に 6、0、3、0、1、1 となります。したがって、求める合計は 6 + 0 + 3 + 0 + 1 + 1 = 11 です。
アプローチ:Zアルゴリズムによる線形時間の解法
この問題は Zアルゴリズム の考え方を利用すると効率的に解けます。Z配列の第 i 要素 z[i] は「位置 i から始まる接尾辞と、文字列の先頭が何文字一致しているか」を表します。これはまさに、今回求めたい「接尾辞との類似度」そのものです。
各接尾辞に対して毎回先頭から素朴に比較すると計算量は O(n²) になりますが、Zアルゴリズムでは、すでに判明している一致区間 [l, r] の情報を再利用することで、文字列全体を O(n) の線形時間で処理できます。
解き方の手順
- length := 文字列 s の長さ
- total := length(最初の接尾辞は文字列自身なので、類似度は length になる。これを初期値とする)
- z := 0 を1つ含むリストとして初期化
- l := 0、r := 0(現在わかっている一致区間の左右端)
- k を 1 から length − 1 まで繰り返します。
- k > r の場合(過去の情報が使えないため新規に比較):
match := 0、index := k とし、index < length のあいだ s[index] と s[match] を比較しながら両方を進めます。不一致になったらループを抜けます。 - 求めた match を z の末尾に追加します。
- match > 0 の場合は、total に match を加算し、l := k、r := index − 1 として一致区間を更新します。
- k ≤ r の場合(既存の一致区間を再利用できる場合):
- z[k−l] < (r−k)+1 であれば、z[k−l] をそのまま z の末尾に追加し、total に加算します。
- そうでなければ、match := r−k、index := r から比較を再開し、不一致まで進めた結果を z に追加して total に加算し、l := k、r := index − 1 を更新します。
- k > r の場合(過去の情報が使えないため新規に比較):
- 最後に total を返します。
Pythonでの実装例
以下が実際の実装です。
def solve(s):
length = len(s)
total = length
z = [0]
l = 0
r = 0
for k in range(1, length):
if k > r:
# 既存の一致区間が使えないため、先頭から直接比較する
match = 0
index = k
while index < length:
if s[index] == s[match]:
match += 1
index += 1
else:
break
z.append(match)
if match > 0:
total += match
l = k
r = index - 1
else:
# 一致区間 [l, r] の情報を再利用する
if z[k - l] < (r - k) + 1:
z.append(z[k - l])
total += z[k - l]
else:
match = r - k
index = r
while index < length:
if s[index] == s[match]:
match += 1
index += 1
else:
break
z.append(match)
total += match
l = k
r = index - 1
return total
s = "pqpqpp"
print(solve(s))
入力
"pqpqpp"
出力
11
まとめ
文字列とそのすべての接尾辞との類似度の合計を求める問題は、Zアルゴリズムを使うことで O(n) の計算量で解くことができます。ポイントは、すでに判明している一致区間 [l, r] の情報を再利用して、重複する比較を省略することです。この考え方は、文字列検索やパターンマッチングなど、さまざまな文字列処理の問題にも応用できるので、ぜひ押さえておきましょう。
-
Pythonで文字列内に最も多く出現する文字とその出現回数を求める方法
この記事では、文字列の中で最も多く出現する文字と、その出現回数を求める方法について、考え方と実装手順をわかりやすく解説します。 問題文 入力として与えられた文字列から、最も多く出現する文字と、その出現回数を特定します。 アプローチ Python標準ライブラリの collections.Counter を使い、「文字をキー・出現回数を値」とする辞書を作成します。 辞書の値(出現回数)の中から最大値を求め、その最大値に対応する文字を取得します。 それでは、実際の実装例を見ていきましょう。 実装例 from collections import Counter def find(input_)
-
Pythonで文字列を分割・結合する方法|split()とjoin()の使い方を解説
Pythonには、文字列の分割(split)と結合(join)を行うための組み込みメソッドが標準で用意されています。split()メソッドを使えば文字列を任意の区切り文字で簡単に分割でき、join()メソッドを使えば複数の文字列をひとつに連結できます。本記事では、それぞれの基本的な使い方をサンプルコードとともに解説します。 split Str.split() join Str1.join(str2) アルゴリズム ステップ1:文字列を入力する。 ステップ2:分割にはsplitメソッドを、結合にはjoin関数を使用する。 ステップ3:結果を出力する。 各メソッドのポイント split()