1 条题解
-
0
思路与算法
把所有模式插入Trie并记录每个查询项的终止节点;重复字符串保留多个终止节点记录。BFS构造fail指针,扫描文章时沿Trie/失配边更新状态,对经过的状态计数。最后按BFS逆序把计数加到fail父节点,每个查询终止节点的计数>0即这项查询出现过,累加这些布尔值。采用首孩子/兄弟链的稀疏转移,降低内存。
正确性说明
扫描前缀后的状态表示其最长Trie后缀。一个模式在当前位置结束当且仅当其终止节点位于当前状态的fail链。逆BFS聚合将每次状态访问传给全部fail祖先,故每个终止节点的最终计数正好为该模式的出现次数。只判断是否大于0而不是直接累加次数,满足“出现了多少待查询单词”;相同查询项各自计一次。
复杂度
设模式总长S、文章长M、字母表σ=26,节点数V≤S+1。稀疏边查找最多σ步,插入、构造及扫描的上界可按各模式路径和文章上的失配摊还为O(σ(S+M)),BFS聚合O(V),初始化节点与读取O(S+M)。空间O(S+M+n),节点按实际总长预留,每节点约32字节及4字节BFS序,完整范围适配64MiB。
易错点
文章反复出现同一模式只给这一查询项贡献1,查询词重复则每个输入查询项分别计算。fail聚合不能正序进行。多组之间重建自动机,首行T用文本读入,之后按合法协议读到EOF处理各组,避免凭空给未限组数加int上界。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct Node { int first = -1, next = -1, fail = 0; char ch = 0; long long count = 0; }; struct AC { vector<Node> v; vector<int> order; AC(size_t cap) { v.reserve(cap); v.emplace_back(); } int edge(int u, char ch) { for (int x = v[u].first; x != -1; x = v[x].next) if (v[x].ch == ch) return x; return -1; } int insert(const string &s, bool mark) { int u = 0; for (char ch : s) { int x = edge(u, ch); if (x < 0) { x = v.size(); Node node; node.ch = ch; node.next = v[u].first; v.push_back(node); v[u].first = x; } u = x; if (mark) v[u].count++; } return u; } void build() { order.reserve(v.size()); for (int x = v[0].first; x != -1; x = v[x].next) order.push_back(x); for (size_t i = 0; i < order.size(); ++i) { int u = order[i]; for (int x = v[u].first; x != -1; x = v[x].next) { int f = v[u].fail; while (f && edge(f, v[x].ch) < 0) f = v[f].fail; int t = edge(f, v[x].ch); v[x].fail = t < 0 ? 0 : t; order.push_back(x); } } } int step(int u, char ch) { while (u && edge(u, ch) < 0) u = v[u].fail; int x = edge(u, ch); return x < 0 ? 0 : x; } void aggregate() { for (size_t i = order.size(); i > 0; --i) { int u = order[i - 1]; v[v[u].fail].count += v[u].count; } } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string T; cin >> T; int n; while (cin >> n) { vector<string> w(n); size_t total = 1; for (auto &s : w) { cin >> s; total += s.size(); } AC ac(total); vector<int> end; for (auto &s : w) end.push_back(ac.insert(s, false)); ac.build(); string text; cin >> text; int u = 0; for (char ch : text) { u = ac.step(u, ch); ac.v[u].count++; } ac.aggregate(); int ans = 0; for (int x : end) ans += ac.v[x].count > 0; cout << ans << '\n'; } }
- 1