Pythonで文字列内にアナグラムが存在するすべての部分文字列を検索するプログラム
小文字のみで構成された文字列 s が与えられます。ここで求めたいのは、文字列内の別の場所に、その部分文字列のアナグラム(文字を並べ替えたもの)が必ず存在するような部分文字列をすべて見つけ、辞書順(辞書式順序)にソートしたリストとして返すことです。
たとえば、入力が s = "abcba" の場合、出力は以下のようになります。
['a', 'a', 'ab', 'abc', 'abcb', 'b', 'b', 'ba', 'bc', 'bcba', 'cb', 'cba']
これらの各部分文字列について、元の文字列内の異なる位置に対応するアナグラムが実際に存在することを確認できます。なお、同じ文字(たとえば 'a')が複数回現れるのは、出現位置ごとに個別にカウントしているためです。
解法の考え方
ポイントは、「2つの文字列がアナグラムの関係にある ⇔ 文字をソートすると一致する」という性質です。この性質を利用して、同じキーを持つ部分文字列同士をグループ化していきます。具体的には、次の手順に従います。
res:= 結果を格納するための新しいリストL:= 文字列sの長さi を 1 から L まで繰り返す(取り出す部分文字列の長さ)
smap:= 値がリスト型である空の辞書(defaultdict)を用意j を 0 から L − i まで繰り返す(部分文字列の開始位置)
cs:=sのインデックス j から j + i − 1 までの部分文字列k:=csの文字をソートして連結した文字列(アナグラム判定用の正規形キー)csをsmap[k]の末尾に追加
smap内の各キーkと値vについてvの要素数が 2 以上であれば、vの要素をすべてresに追加
resをソートして返す
実装例
以下のPythonコードを見ると、処理の流れがより明確になります。
from collections import defaultdict
def solve(s):
res = []
L = len(s)
for i in range(1, L + 1):
smap = defaultdict(list)
for j in range(L - i + 1):
cs = s[j : j + i]
k = "".join(sorted(cs))
smap[k].append(cs)
for k, v in smap.items():
if len(v) >= 2:
res.extend(v)
return sorted(res)
s = "abcba"
print(solve(s))
入力
"abcba"
出力
['a', 'a', 'ab', 'abc', 'abcb', 'b', 'b', 'ba', 'bc', 'bcba', 'cb', 'cba']
計算量の目安
この方法では、考えられるすべての長さ・開始位置の組み合わせについて部分文字列を生成し、それぞれをソートしてキーを作るため、時間計算量はおおよそ O(n³ log n) になります(n は文字列の長さ)。シンプルな全探索的なアプローチですが、ロジックが分かりやすく、中程度の長さの文字列であれば十分に実用的です。より大きな入力を扱う場合は、ローリングハッシュなどでキー計算を高速化する工夫も有効です。
-
Pythonでリスト内の文字列から指定文字列に近い一致をすべて見つける方法
はじめに このチュートリアルでは、「文字列のリストの中から、指定された要素に近い一致をする文字列をすべて見つける」という問題の解決策を紹介します。まず、問題の内容を確認してみましょう。 問題の概要 文字列のリストと1つの要素が与えられたとき、リストの中からその要素と近い一致となる文字列を探し出します。以下の例を見てください。 入力: strings = ["Lion", "Li", "Tiger", "Tig"] element = "Lion" 出力: Lion Li 解決のアプローチ
-
Pythonで文字列内のミラー文字を検索する方法【初心者向け解説】
ユーザーが入力した文字列と位置(ポジション)が与えられたとき、その位置から文字列の末尾までの文字を、アルファベット順を反転させた「ミラー文字」に変換するプログラムを作成します。この操作では、「a」→「z」、「b」→「y」、「c」→「x」、「d」→「w」のように、アルファベットの最初の文字が最後の文字に対応する形で置き換えを行います。 入力: p = 3 入力文字列 = python 出力: pygslm 上記の例では、3番目の位置以降の文字「t」「h」「o」「n」が、それぞれ逆順のアルファベット「g」「s」「l」「m」に変換されていることがわかります。先頭から指定位置までは元の文字列