CF547E Mike y sus amigos (autómata de CA)

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]);
}

Supongo que te gusta

Origin www.cnblogs.com/Hs-black/p/12672461.html
Recomendado
Clasificación