CF547E Mike y sus amigos (autómata de CA + agrupación binaria)
Asunto
Dadas \ (n \) cadenas \ (s_ {1 \ dots n} \) .
\ (q \) veces se le preguntó cuántas veces \ (s_k \) apareció en \ (s_ {l \ dots r} \) .
\ (n, \ sum | s | \ le 2 \ times 10 ^ 5 \) , \ (q \ le 5 \ times 10 ^ 5 \) .
Solución
La esencia del puntero de falla del autómata de CA es encontrar la cadena formada desde la raíz hasta el nodo actual. El prefijo más largo lo hace igual a su sufijo de la misma longitud, es decir, KMP se coloca en el árbol
¿Cómo juzgar si una cadena T contiene otra cadena S? Es fácil saber que S debe ser el sufijo de un prefijo de T. En mi opinión, el nodo de terminación de un prefijo de T siempre salta el puntero de falla, y definitivamente puede saltar a S. Los nodos que alcanzas pueden ser simulados por ti mismo
Si este es el caso, la pregunta debería estar repentinamente fuera de línea, y el autómata de CA debería construirse para tomar todas las cadenas y encontrar el orden dfs. Agregue una cadena para hacer que el nodo prefijo +1, y observe el peso del subárbol.
El código es el siguiente:
#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]);
}