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)))