結果
| 問題 | No.430 文字列検索 |
| コンテスト | |
| ユーザー |
👑 |
| 提出日時 | 2026-01-02 17:18:27 |
| 言語 | PyPy3 (7.3.17) |
| 結果 |
AC
|
| 実行時間 | 213 ms / 2,000 ms |
| コード長 | 3,912 bytes |
| 記録 | |
| コンパイル時間 | 483 ms |
| コンパイル使用メモリ | 82,660 KB |
| 実行使用メモリ | 92,516 KB |
| 最終ジャッジ日時 | 2026-01-02 17:18:31 |
| 合計ジャッジ時間 | 3,789 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 14 |
ソースコード
from collections import deque
from string import ascii_lowercase, ascii_uppercase
from typing import Iterable, TypeVar
T = TypeVar("T")
class AhoCorasick:
"""\
Aho-Corasick 法
text 中に含まれる複数の pattern を検索するアルゴリズム
Usage::
from string import ascii_uppercase
ac = AhoCorasick(ascii_uppercase)
ac.add("ABC")
ac.add("B")
ac.add("ZZZZZ")
ac.build()
print(ac.find_all("XBABABCDEX"))
# => [[4], [1, 3, 5], []]
"""
# Aho-Corasick グラフにおける根の頂点番号
ROOT: int = 0
# 文字の種類数
sigma: int
# Aho-Corasick グラフ
graph: list[list[int | None]]
# 頂点番号と検索パターン文字列の suffix の対応
end: list[set[int]]
# 各検索パターン文字列の長さ
length: list[int]
def __init__(self, letters: Iterable[T] = ascii_lowercase):
"""\
Aho-Corasick 法
Args::
letters: 使用する文字の種類 (デフォルト: 英小文字)
"""
self.sigma = len(letters)
self.to_index = {s: i for i, s in enumerate(letters)}
self.graph = [[None] * self.sigma]
self.end = [set()]
self.length = []
def add(self, pattern: Iterable[T]):
"""\
検索したい文字列 pattern を追加する
Args::
pattern: 検索したい文字列
Complexity::
O(sigma * len(pattern))
"""
self.length.append(len(pattern))
self._add(pattern, len(self.length) - 1)
def _add(self, S: Iterable[T], id: int):
curr = self.ROOT
for s in S:
index = self.to_index[s]
if (to := self.graph[curr][index]) is None:
self.graph.append([None] * self.sigma)
self.end.append(set())
to = len(self.graph) - 1
self.graph[curr][index] = to
curr = to
self.end[curr].add(id)
def build(self):
"""\
Aho-Corasick グラフを構築する
Complexity::
O(sigma * sum(len(pattern)))
"""
link = [self.ROOT] * len(self.graph)
que: deque[int] = deque()
for i in range(self.sigma):
if self.graph[self.ROOT][i] is None:
self.graph[self.ROOT][i] = self.ROOT
else:
link[self.graph[self.ROOT][i]] = self.ROOT
que.append(self.graph[self.ROOT][i])
while que:
curr = que.popleft()
self.end[curr].update(self.end[link[curr]])
for i in range(self.sigma):
to = self.graph[curr][i]
if to is None:
self.graph[curr][i] = self.graph[link[curr]][i]
else:
link[to] = self.graph[link[curr]][i]
que.append(to)
def find_all(self, text: Iterable[T], start: int = 0) -> list[list[int]]:
"""\
各パターンについて、text 中に含まれるパターンの開始位置の配列を返す
Args::
text: 文字列
Complexity::
O(len(text) + sum(len(pattern)) + match回数)
"""
found = [[] for _ in range(len(self.length))]
curr = start
for i, char in enumerate(text):
index = self.to_index[char]
curr = self.graph[curr][index]
for pattern_id in self.end[curr]:
found[pattern_id].append(i - self.length[pattern_id] + 1)
return found
S = input()
M = int(input())
ac = AhoCorasick(ascii_uppercase)
for _ in range(M):
C = input()
ac.add(C)
ac.build()
print(sum(len(f) for f in ac.find_all(S)))