-------------------------------------- 过年在家无聊补一下这周做的几道AC自动机的模板题
sol:AC自动机,还是要解决跳fail边产生的重复访问,但是这次用last边已经不行了,只能拿76分。我们把跳fail边的过程放到串扫描完之后一次性进行。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 200010; struct Trie { int son[MAXN][26], fail[MAXN]; int que[MAXN]; int head, tail; int cnt[MAXN]; int tot, root; int add_node() { memset(son[tot], -1, sizeof(son[tot])); cnt[tot] = 0; return tot ++; } void init() { head = tail = 0; tot = 0; root = add_node(); } int insert(char* s) { int p = root; for (int i = 0; s[i]; i++) { int index = s[i] - ‘a‘; if (son[p][index] == -1) son[p][index] = add_node(); p = son[p][index]; } return p; } void build() { fail[root] = root; for (int i = 0; i < 26; i++) { if (son[root][i] == -1) son[root][i] = root; else { fail[son[root][i]] = root; que[++ tail] = son[root][i]; } } while (head != tail) { int p = que[++ head]; for (int i = 0; i < 26; i++) { if (son[p][i] == -1) son[p][i] = son[fail[p]][i]; else { fail[son[p][i]] = son[fail[p]][i]; que[++ tail] = son[p][i]; } } } } void slove(char* s) { int p = root; for (int i = 0; s[i]; i++) { int index = s[i] - ‘a‘; p = son[p][index]; cnt[p] ++; } for (int i = tail; i >= 1; i--) { int p = que[i]; cnt[fail[p]] += cnt[p]; } } } ac; int inde[MAXN]; // 本地运行没什么问题,洛谷叫index就报编译错误 char s[2000010]; int main() { int n; ac.init(); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%s", s); inde[i] = ac.insert(s); } ac.build(); scanf("%s", s); ac.slove(s); for (int i = 1; i <= n; i++) printf("%d\n", ac.cnt[inde[i]]); return 0; }
经过不断地40分、60分、76分,终于搞定了。为了在最后跳fail边的时候保证深度越高的节点越先跳采用了手写队列保留bfs时候的层次关系。网上看到别人的代码据说是用拓扑的,没细看。不过感觉这里的队列就是一个对深度的拓扑排序了。
原文:https://www.cnblogs.com/Angel-Demon/p/12233327.html