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]);
}
posted @ 2020-04-10 11:52  Hs-black  阅读(222)  评论(0编辑  收藏  举报