結果

問題 No.430 文字列検索
ユーザー kainadekainade
提出日時 2024-07-25 06:01:28
言語 PyPy3
(7.3.15)
結果
AC  
実行時間 164 ms / 2,000 ms
コード長 3,242 bytes
コンパイル時間 221 ms
コンパイル使用メモリ 82,472 KB
実行使用メモリ 89,372 KB
最終ジャッジ日時 2024-08-15 16:46:38
合計ジャッジ時間 2,764 ms
ジャッジサーバーID
(参考情報)
judge4 / judge5
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 75 ms
76,996 KB
testcase_01 AC 36 ms
55,928 KB
testcase_02 AC 164 ms
89,372 KB
testcase_03 AC 101 ms
79,496 KB
testcase_04 AC 99 ms
79,440 KB
testcase_05 AC 36 ms
55,004 KB
testcase_06 AC 35 ms
54,700 KB
testcase_07 AC 34 ms
55,076 KB
testcase_08 AC 35 ms
55,688 KB
testcase_09 AC 66 ms
76,188 KB
testcase_10 AC 49 ms
66,440 KB
testcase_11 AC 59 ms
71,828 KB
testcase_12 AC 159 ms
84,908 KB
testcase_13 AC 158 ms
86,188 KB
testcase_14 AC 161 ms
86,188 KB
testcase_15 AC 143 ms
83,624 KB
testcase_16 AC 136 ms
81,344 KB
testcase_17 AC 142 ms
81,592 KB
testcase_18 AC 127 ms
81,340 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

"""
NOTE: examples.

# explore
https://atcoder.jp/contests/abc268/submissions/55937468

# fail_tree
https://atcoder.jp/contests/abc362/submissions/55937489

# calc_all_matching_ranges
https://atcoder.jp/contests/abc268/submissions/55937440
"""
from collections import deque
class AhoCorasick:
	class Node:
		def __init__(self, node_id):
			self.id = node_id
			self.trie_par = None
			self.trie_ch = {}
			self.rep_patterns = []
			self.suf_patterns = []

			self.fail_par = None

		def __getitem__(self, i):
			return self.trie_ch[i]
		def __setitem__(self, i, value):
			self.trie_ch[i] = value
			return self
		def __contains__(self, c):
			return c in self.trie_ch

	def __init__(self, patterns, fail_tree=False):
		self.nodes = [self.Node(0)]
		self.patterns = patterns

		for pi, pat in enumerate(self.patterns):
			self.__add_pattern(pat, pi)
		self.__build_fail_par_and_suf_patterns()
		self.N = len(self.nodes)
		if fail_tree:
			self.build_fail_tree()

	def __add_pattern(self, pat, pi):
		Node, nodes = self.Node, self.nodes

		node = nodes[0]
		for c in pat:
			if c in node:
				node = node[c]
			else:
				new = Node(len(nodes))
				new.trie_par = node
				nodes.append(new)
				node[c] = new
				node = node[c]

		node.rep_patterns.append(pi)
		node.suf_patterns.append(pi)

	def __build_fail_par_and_suf_patterns(self):
		nodes, get_next = self.nodes, self.get_next

		root = nodes[0]
		deq = deque()
		for cn in root.trie_ch.values():
			cn.fail_par = root
			deq.append(cn)

		while deq:
			node = deq.popleft()
			f = node.fail_par
			for c, cn in node.trie_ch.items():
				cn.fail_par = get_next(f, c)
				# 同一の集合は同一のインスタンスを使いまわし、空間をpatternsのサイズに抑える.
				if len(cn.rep_patterns) > 0:
					cn.suf_patterns.extend(cn.fail_par.suf_patterns)
				else:
					cn.suf_patterns = cn.fail_par.suf_patterns
				deq.append(cn)

	def build_fail_tree(self):
		nodes = self.nodes
		if hasattr(nodes[0], "fail_ch"):
			return

		for node in nodes:
			node.fail_ch = []
		for node in nodes[1:]:
			fp = node.fail_par
			fp.fail_ch.append(node)

	def get_next(self, node, c):
		while True:
			if c in node:
				return node[c]
			if node.id == 0:
				return node
			node = node.fail_par

	def explore(self, target):
		nodes, get_next = self.nodes, self.get_next
		node = nodes[0]
		for c in target:
			node = get_next(node, c)
			
			# 途中、根など特定のノードに移動したい時にsendを使用.
			nex = (yield node)
			if nex is not None:
				node = nex

	def calc_dfs_order_on_fail_tree(self):
		nodes = self.nodes
		if not hasattr(nodes[0], "fail_ch"):
			self.build_fail_tree()
		tp = []
		stack = [nodes[0]]
		while stack:
			v = stack.pop()
			tp.append(v)
			for ch in v.fail_ch:
				stack.append(ch)
		return tp

	def calc_all_matching_ranges(self, target):
		explore, patterns = self.explore, self.patterns
		res = []
		for r, node in enumerate(explore(target)):
			for pi in node.suf_patterns:
				res.append((r-len(patterns[pi])+1, r+1, pi))
		return res



def main():
	S = input()
	N = int(input())
	ps = [input() for _ in range(N)]

	aho = AhoCorasick(ps)
	ans = 0
	for node in aho.explore(S):
		ans += len(node.suf_patterns)
	print(ans)

main()
0