Pythonで文字列内の「大きなグループ」の位置を検出するアルゴリズム
小文字の英字のみで構成される文字列 S を考えます。このような文字列では、同じ文字が連続して現れる部分が「グループ」を形成します。たとえば、S = "abbxxxxzyy" の場合、グループは "a"、"bb"、"xxxx"、"z"、"yy" の5つに分けられます。このうち、3文字以上で構成されるグループを「大きなグループ(large group)」と定義します。
本記事では、文字列中に存在するすべての大きなグループについて、その開始位置と終了位置を求めるアルゴリズムを解説します。
たとえば、入力が "abcdddeeeeaabbbcd" の場合、出力は [[3,5],[6,9],[12,14]] となります。これは、インデックス3〜5の "ddd"、インデックス6〜9の "eeee"、インデックス12〜14の "bbb" がそれぞれ3文字以上の連続グループであることを示しています。
解法のアプローチ
この問題は、Python標準ライブラリの itertools.groupby を活用することで、簡潔かつ効率的に解くことができます。手順は以下の通りです。
- 結果を格納するための空のリスト
ansを用意します。 - ここまで処理した文字数を追跡するカウンター
csumを 0 で初期化します。 groupbyを使って、連続する同一文字ごとに文字列をグループ化します。- 各グループについて、サイズが3以上であれば、開始位置
csumと終了位置csum + グループサイズ - 1のペアをansに追加します。 csumにグループのサイズを加算し、次のグループへ進みます。- すべてのグループを処理したら、
ansを返します。
実装例
以下が実際のPythonコードです。
from itertools import groupby
class Solution:
def largeGroupPositions(self, S):
ans = []
csum = 0
for a, b in groupby(S):
grp = list(b)
if len(grp) >= 3:
ans.append([csum, csum + len(grp) - 1])
csum += len(grp)
return ans
ob = Solution()
print(ob.largeGroupPositions("abcdddeeeeaabbbcd"))
入力と出力
入力
"abcdddeeeeaabbbcd"
出力
[[3, 5], [6, 9], [12, 14]]
コードのポイント
itertools.groupby は、隣接する同一要素をまとめてイテレートできる便利な関数です。各グループのキー(文字)とグループ本体を受け取り、list(b) でグループをリスト化することで長さを取得できます。
計算量は文字列の長さを n とすると O(n) となり、各文字を一度だけ走査すればよいため、非常に効率的です。また、累積カウンター csum を使うことで、各グループの開始インデックスを毎回計算し直す必要がない点もポイントです。
-
【Python入門】正規表現のgroups()メソッドの使い方をわかりやすく解説
re.groups()メソッドとはre.groups()メソッドは、マッチオブジェクトに含まれるすべてのサブグループ(キャプチャグループ)を、1番目からパターン内のグループ数まで順に格納したタプルとして返します。マッチに参加しなかったグループにはdefault引数で指定した値が使われ、引数を省略した場合はNoneが返されます。なお、パターン内にグループが1つしか存在しない場合でも、戻り値は必ずタプル形式で返される点に注意してください。基本的な使用例>>> m = re.match(r(\d+)\.(\d+), 27.1835) >>> m.groups()
-
Pythonでファイルやディレクトリの所有者を変更する方法
Pythonでは、標準ライブラリの pwd、grp、os の3つのモジュールを組み合わせることで、ファイルやディレクトリの所有者(オーナー)を変更することができます。各モジュールの役割pwdモジュール: ユーザー名からUID(ユーザーID)を取得しますgrpモジュール: グループ名からGID(グループID)を取得しますosモジュール: os.chown() 関数を使って、実際に所有者を変更しますサンプルコード以下は、ユーザー名「nobody」とグループ名「nogroup」に所有者を変更する例です。import pwd import grp import os # ユーザー名「nobody」か