其他分享
首页 > 其他分享> > 「NOI 2011」阿狸的打字机

「NOI 2011」阿狸的打字机

作者:互联网

Description

给定 nnn 个字符串,mmm 次询问第 xxx 个字符串在第 yyy 个字符串中出现了多少次。n,m105n,m\le10^5n,m≤105

Solution

建立AC自动机和 failfailfail 树,问题转化为:failfailfail 树上 xxx 串的终止节点的子树中有多少个节点属于 yyy 串。

求出 failfailfail 树的 dfsdfsdfs 序,将询问离线,并在 yyy 的终止节点处打上标记。

在AC自动机上 dfsdfsdfs,每到达一个点就在树状数组中将其 dfsdfsdfs 序 +1+1+1,离开时再 1-1−1,这样树状数组中存的就是当前点到 000 号点的链,若当前点有标记,则在树状数组上查询 xxx 的子树的和即可。

#include <queue>
#include <cstdio>
#include <cstring>

const int N = 100002;

struct List {
	struct Edge {
		int v, nxt;
	} e[N];
	int head[N], tot;
	void adde(int u, int v) {
		e[++tot].nxt = head[u], head[u] = tot, e[tot].v = v;
	}
} a, b;

int siz[N], dfn[N], cnt, ch[N][26], tr[N][26];
int fa[N], fail[N], ed[N], ans[N], sum[N], sz;
char s[N];

int read() {
	int x = 0; char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9')
		x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	return x;
}
void update(int x, int y) {
	while (x <= sz + 1)
		sum[x] += y, x += x & (-x);
}
int query(int x) {
	int res = 0;
	while (x)
		res += sum[x], x -= x & (-x);
	return res;
}
void build() {
	int n = strlen(s), x = 0;
	for (int i = 0; i < n; ++i) {
		if (s[i] == 'P') ed[++ed[0]] = x;
		else if (s[i] == 'B') x = fa[x];
		else {
			int c = s[i] - 'a';
			if (!ch[x][c])
				tr[x][c] = ch[x][c] = ++sz, fa[ch[x][c]] = x;
			x = ch[x][c];
		}
	}
	for (int i = 0; i <= sz; ++i) a.head[i] = b.head[i] = -1;
	std::queue<int> q;
	for (int i = 0; i < 26; ++i)
		if (ch[0][i]) a.adde(0, ch[0][i]), q.push(ch[0][i]);
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = 0; i < 26; ++i) {
			if (!ch[x][i])
				ch[x][i] = ch[fail[x]][i];
			else {
				fail[ch[x][i]] = ch[fail[x]][i];
				a.adde(fail[ch[x][i]], ch[x][i]), q.push(ch[x][i]);
			}
		}
	}
}
void dfs1(int u) {
	siz[u] = 1, dfn[u] = ++cnt;
	for (int i = a.head[u]; ~i; i = a.e[i].nxt)
		dfs1(a.e[i].v), siz[u] += siz[a.e[i].v];
}
void dfs2(int u) {
	update(dfn[u], 1);
	for (int i = b.head[u]; ~i; i = b.e[i].nxt)
		ans[i] = query(dfn[b.e[i].v] + siz[b.e[i].v] - 1) - query(dfn[b.e[i].v] - 1);
	for (int i = 0; i < 26; ++i)
		if (tr[u][i]) dfs2(tr[u][i]);
	update(dfn[u], -1);
}
int main() {
	scanf("%s", s), build(), dfs1(0);
	int m = read();
	for (int i = 1, x, y; i <= m; ++i)
		x = read(), y = read(), b.adde(ed[y], ed[x]);
	dfs2(0);
	for (int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	return 0;
}

标签:26,ch,NOI,阿狸,int,++,dfn,fail,2011
来源: https://blog.csdn.net/Milkyyyyy/article/details/89522588