defget_p(s: str) -> list[int]: p = [0] * len(s) l = 0 for r inrange(1, len(s)): while l > 0and s[l] != s[r]: l = p[l - 1] if s[l] == s[r]: l += 1 p[r] = l return p
defkmp(s: str, t: str) -> int: p = get_p(t) l = 0 for r inrange(len(s)): while l > 0and t[l] != s[r]: l = p[l - 1] if t[l] == s[r]: l += 1 if l == len(t): return r - len(t) + 1 return -1
defkmp_all(s: str, t: str) -> list[int]: p = get_p(t) res = [] l = 0 for r inrange(len(s)): while l > 0and t[l] != s[r]: l = p[l - 1] if t[l] == s[r]: l += 1 if l == len(t): res.append(r - len(t) + 1) l = p[l - 1] return res
vector<int> get_p(const string_view s){ vector<int> p(s.size()); int l = 0; for (int r = 1; r < int(s.size()); ++r) { while (l > 0 && s[l] != s[r]) l = p[l - 1]; if (s[l] == s[r]) ++l; p[r] = l; } return p; }
intkmp(const string_view text, const string_view pattern){ auto p = get_p(pattern); int l = 0; for (int r = 0; r < int(text.size()); ++r) { while (l > 0 && pattern[l] != text[r]) l = p[l - 1]; if (pattern[l] == text[r]) ++l; if (l == int(pattern.size())) return r - l + 1; } return-1; }
vector<int> kmp_all(const string_view text, const string_view pattern){ auto p = get_p(pattern); vector<int> pos; int l = 0; for (int r = 0; r < int(text.size()); ++r) { while (l > 0 && pattern[l] != text[r]) l = p[l - 1]; if (pattern[l] == text[r]) ++l; if (l == int(pattern.size())) { pos.push_back(r - l + 1); l = p[l - 1]; } } return pos; }
# build trie for idx, pattern inenumerate(patterns): cur = root for c in pattern: i = ord(c) - ord("a") if cur.ch[i] isNone: nodes.append(TrieNode()) cur.ch[i] = nodes[-1] cur = cur.ch[i] cur.idxes.append(idx)
# build fail pointer root.fail = root q = deque(x for x in root.ch if x isnotNone) for x in q: x.fail = root root.ch = [x if x isnotNoneelse root for x in root.ch] while q: cur = q.popleft() for i inrange(26): if cur.ch[i] isNone: cur.ch[i] = cur.fail.ch[i] else: cur.ch[i].fail = cur.fail.ch[i] cur.fail.ch[i].in_degree += 1 q.append(cur.ch[i])
# search cur = root for c in text: i = ord(c) - ord("a") cur = cur.ch[i] cur.visit_cnt += 1
# count occurrences with topological sort occurrences = [0] * len(patterns) q = deque(x for x in nodes ifnot x.in_degree) while q: cur = q.popleft() for idx in cur.idxes: occurrences[idx] = cur.visit_cnt cur.fail.visit_cnt += cur.visit_cnt cur.fail.in_degree -= 1 ifnot cur.fail.in_degree: q.append(cur.fail)
// build trie for (int idx = 0; idx < int(patterns.size()); ++idx) { TrieNode* cur = nodes.front().get(); for (auto c : patterns[idx]) { int i = c - CHAR_START; if (!cur->ch[i]) cur->ch[i] = new_node(); cur = cur->ch[i]; } cur->idxes.push_back(idx); }
// build fail pointer root->fail = root; queue<TrieNode*> q; for (int i = 0; i < CHAR_SIZE; ++i) { if (root->ch[i]) { root->ch[i]->fail = root; q.push(root->ch[i]); } else { root->ch[i] = root; } } while (!q.empty()) { TrieNode* cur = q.front(); q.pop(); for (int i = 0; i < CHAR_SIZE; ++i) { if (cur->ch[i] == nullptr) { cur->ch[i] = cur->fail->ch[i]; } else { cur->ch[i]->fail = cur->fail->ch[i]; ++cur->fail->ch[i]->in_degree; q.push(cur->ch[i]); } } }
// search TrieNode* cur = root; for (auto c : text) { int i = c - CHAR_START; cur = cur->ch[i]; ++cur->visit_cnt; }
// count occurrences with topological sort vector<int> occurrences(patterns.size()); for (auto& x : nodes) if (x->in_degree == 0) q.push(x.get()); while (!q.empty()) { TrieNode* cur = q.front(); q.pop(); for (int idx : cur->idxes) occurrences[idx] += cur->visit_cnt; cur->fail->visit_cnt += cur->visit_cnt; if (--cur->fail->in_degree == 0) q.push(cur->fail); }
return occurrences; }
intmain(){ ios::sync_with_stdio(false), cin.tie(0); int N; cin >> N; vector<string> patterns(N); for (auto& s : patterns) cin >> s; string text; cin >> text; auto occurrences = ac_automation(patterns, text); cout << count_if(occurrences.begin(), occurrences.end(), [](int x) { return x > 0; }); }