【模板】AC自动机(二次加强版)
题目链接:luogu P5357
题目大意
给出一些字符串和一个大字符串,求每一个字符串在大字符串中出现的次数。
思路
这道题跟前面有一点相像:反正我们上一题其实也求出了每个字符串的出现次数。
但是我们可以发现在这个数据规模下遇到特殊情况会超时,而且我的代码还会 MLE。
MLE 的我们可以改成之前我在最后说的方法来解决(就是把相同的合成一个,然后最后在分开来),但是 TLE 的怎么解决呢?
(在这里说一下会 TLE 的数据,就是都是相同的字符,然后 fail 就只能一个一个跳,就会超时)
我们想一下以某一个位置结尾时,计算中是怎么计算的。
就是沿着 fail 边找到那一条 fail 边组成的链,然后把链上的点都加一次。
那我们怎么让这个操作由找链时的 O(n) 变成更低的呢?
可以想到在这个 “fail 树” 上做前缀和。
那我们怎么还原呢?
就是要跑一遍图,然后某个点加上能跳到他的点的确定最终值。
但是怎么保证你每次找到的点都已经可以加了呢?
可以想到用拓扑序。
然后就实现,就可以了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
struct node {
int num, fail, kind;
int son[31];
}tree[2000001];
int n, size, now, thi, KK, ru[2000001], pla[2000001], real[2000001];
queue <int> q;
char c[2000001];
void build_Trie(int which) {
now = 0;
size = strlen(c);
for (int i = 0; i < size; i++) {
thi = c[i] - 'a';
if (!tree[now].son[thi]) tree[now].son[thi] = ++KK;
now = tree[now].son[thi];
}
if (!tree[now].kind) tree[now].kind = which;
pla[which] = tree[now].kind;
}
void get_fail() {
for (int i = 0; i < 26; i++)
if (tree[0].son[i]) {
q.push(tree[0].son[i]);
tree[tree[0].son[i]].fail = 0;
ru[tree[tree[0].son[i]].fail]++;
}
while (!q.empty()) {
now = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
if (tree[now].son[i]) {
tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
q.push(tree[now].son[i]);
ru[tree[tree[now].son[i]].fail]++;
}
else tree[now].son[i] = tree[tree[now].fail].son[i];
}
}
}
void get_AC() {
size = strlen(c);
now = 0;
for (int i = 0; i < size; i++) {
thi = c[i] - 'a';
now = tree[now].son[thi];
tree[now].num++;
}
}
void tuopu() {//利用拓扑,实现前缀和的还原
for (int i = 0; i <= KK; i++)
if (!ru[i])
q.push(i);
while (!q.empty()) {
now = q.front();
q.pop();
real[tree[now].kind] = tree[now].num;
ru[tree[now].fail]--;
if (!ru[tree[now].fail]) {
q.push(tree[now].fail);
}
tree[tree[now].fail].num += tree[now].num;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", &c);
build_Trie(i);
}
get_fail();
scanf("%s", &c);
get_AC();
tuopu();
for (int i = 1; i <= n; i++)
printf("%d\n", real[pla[i]]);
return 0;
}