CF547E Mike and Friends(AC自动机)
CF547E Mike and Friends(AC自动机+二进制分组)
题目大意
给定 \(n\) 个字符串 \(s_{1 \dots n}\)。
\(q\) 次询问 \(s_k\) 在 \(s_{l \dots r}\) 中出现了多少次。
\(n, \sum |s| \le 2 \times 10^5\),\(q \le 5 \times 10^5\)。
题解
AC自动机fail指针的本质是找出从根到当前节点所形成的字符串中, 最长的前缀使得和其相同长度后缀相等, 就是将KMP放到了树上
如何判断一个字符串T包含另一个字符串S呢? 易知S肯定是T一个前缀的后缀, 以笔者之见, T的某个前缀的终止节点一直向上跳fail指针, 肯定能跳到S所到达的节点, 可以自己模拟一下就行
如此则本题豁然, 首应离线, 建AC自动机以所有字符串, 求之dfs序, 加入一字符串则使其前缀节点+1, 询之而看子树权值即可
代码如下:
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template <typename T>
void write(T x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 505000;
char tmp[N];
int f[N], fa[N], tot, n, Q;
int ch[N][26], en[N];
int siz[N], dfn[N], num;
int add_c(char *s, int len) {
int p = 0;
for (int i = 0;i < len; i++) {
int di = s[i] - 'a';
if (!ch[p][di]) fa[ch[p][di] = ++tot] = p;
p = ch[p][di];
}
return p;
}
int h[N], ne[N], to[N], et;
inline void add_e(int x, int y) {
ne[++et] = h[x], to[h[x] = et] = y;
}
queue<int> q;
void work(void) {
for (int i = 0;i < 26; i++)
if (ch[0][i]) q.push(ch[0][i]);
while (q.size()) {
int x = q.front(); q.pop();
add_e(f[x], x);
for (int i = 0;i < 26; i++) {
if (ch[x][i]) {
f[ch[x][i]] = ch[f[x]][i];
q.push(ch[x][i]);
}
else ch[x][i] = ch[f[x]][i];
}
}
}
void dfs(int x) {
dfn[x] = ++num, siz[x] = 1;
for (int i = h[x]; i; i = ne[i])
dfs(to[i]), siz[x] += siz[to[i]];
}
int d[N];
void Add(int x, int k) {
for (; x <= tot+1; x += x & -x) d[x] += k;
}
ll sum(int x) {
ll tmp = 0;
for (; x; x -= x & -x) tmp += d[x];
return tmp;
}
struct node {
int k, num, f;
};
vector<node > ask[N];
ll ans[N];
int main() {
read(n), read(Q);
for (int i = 1;i <= n; i++) {
scanf ("%s", tmp);
en[i] = add_c(tmp, strlen(tmp));
} work(); dfs(0);
for (int i = 1;i <= Q; i++) {
int l, r, x; read(l), read(r), read(x);
ask[l-1].push_back((node){x, i, -1});
ask[r].push_back((node){x, i, 1});
}
for (int i = 1;i <= n; i++) {
int x = en[i];
while (x) Add(dfn[x], 1), x = fa[x];
for (auto j: ask[i]) {
int k = en[j.k];
ans[j.num] += j.f * (sum(dfn[k] + siz[k] - 1) - sum(dfn[k] - 1));
}
}
for (int i = 1;i <= Q; i++) printf ("%lld\n", ans[i]);
}