1. 程式人生 > 實用技巧 >[AC自動機 fail樹] [NOI2011]阿狸的打字機

[AC自動機 fail樹] [NOI2011]阿狸的打字機

題目

題面
大意就是給定若干字串(輸入方式需要用\(trie\)處理) 然後多組詢問求第\(x\)個列印的串在第\(y\)個列印的串出現幾次。

題解

先建出\(AC\)自動機
考慮第\(x\)個列印的串在第\(y\)個列印的串出現幾次,即等價於有多少屬於\(y\)的字串的\(fail\)指標指向\(x\)的尾結點。
多對一不妨變為一對多的考慮,所以建立\(fail\)樹。
那麼就便成了\(fail\)樹上\(x\)的尾結點指向多少屬於\(y\)的字串。
\(fail\)樹上\(x\)的子樹中有多少個屬於\(y\)的字串。

那麼現在問題就轉化成了 給定一棵樹,多組詢問,求\(x\)子樹下第\(y\)

種顏色出現的次數(但是一個點可能會有多種顏色 坑!)

所以我們離線處理,在\(trie\)上走,走到\(y\)字串結束時 計算每個\(x\)子樹中的貢獻。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>

using namespace std;

template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}

typedef long long ll;

const int INF = 2139062143;

#define DEBUG(x) std::cerr << #x << " = " << x << std::endl

template <typename T> void read (T &x) {
    x = 0; bool f = 1; char ch;
    do {ch = getchar(); if (ch == '-') f = 0;} while (ch > '9' || ch < '0');
    do {x = x * 10 + ch - '0'; ch = getchar();} while (ch >= '0' && ch <= '9');
    x = f ? x : -x;
}

template <typename T> void write (T x) {
    if (x < 0) x = ~x + 1, putchar ('-');
    if (x > 9) write (x / 10);
    putchar (x % 10 + '0');
}

const int N = 5e5 + 7;

int n, cnt, l, num, E, dfs_clock, in[N], fa[N], ans[N], out[N], dfn[N], head[N], fail[N], word[N], cpy[N][26], trie[N][26];
char s[N], p[N];
string ch[N];
vector < int > v[N];

struct EDGE {
	int to, nxt;
} edge[N << 1];

struct Node {
	int x, y;
} a[N];

struct BIT {
	int sz, c[N];
	inline int lowbit(int x) {
		return x & -x;
	}
	inline void add(int i, int x) {
		for(; i <= sz; i += lowbit(i)) c[i] += x;
	}
	inline int sum(int i) {
		int ret = 0;
		for(; i; i -= lowbit(i)) ret += c[i];
		return ret;
	}
	inline int query(int l, int r) {
		return sum(r) - sum(l - 1);
	}
} bit;

inline void addedge(int u, int v) {
	edge[++E].to = v;
	edge[E].nxt = head[u];
	head[u] = E;
}

inline void insert(char *s) {
	int len = strlen(s), now = 0;
	for(int i = 0; i < len; i++) {
		if(s[i] == 'B') {now = fa[now]; continue;}
		if(s[i] == 'P') {word[++num] = now; continue;}
		int v = s[i] - 'a';
		if(!trie[now][v]) trie[now][v] = ++ cnt;
		fa[trie[now][v]] = now;
		now = trie[now][v];
	}
	memcpy(cpy, trie, sizeof(trie));
}

inline void build() {
	queue < int > q;
	for(int i = 0; i < 26; i++) {
		if(trie[0][i]) {
			q.push(trie[0][i]);
			fail[trie[0][i]] = 0;
		}
	}
	while(!q.empty()) {
		int now = q.front(); q.pop();
		for (int i = 0; i < 26; i++) {
			if(trie[now][i]) {
				q.push(trie[now][i]);
				fail[trie[now][i]] = trie[fail[now]][i];
			} else {
				trie[now][i] = trie[fail[now]][i];
			}
		}
	}
	for(int i = 1; i <= cnt; i++) addedge(fail[i], i);
}

inline void dfs(int u) {
	in[u] = ++ dfs_clock;
	for(int i = head[u]; i; i = edge[i].nxt) dfs(edge[i].to);
	out[u] = dfs_clock;
}

inline void solve(int now) {
	bit.add(in[now], 1);
	for(int i = 0; i < v[now].size(); i++) ans[v[now][i]] = bit.query(in[word[a[v[now][i]].x]], out[word[a[v[now][i]].x]]);
	for(int i = 0; i < 26; i++) if(cpy[now][i]) solve(cpy[now][i]);
	bit.add(in[now], -1);
}

int main() {
	scanf("%s", s); insert(s);
	build(); dfs(0); read(n);
	bit.sz = dfs_clock;
	for(int i = 1; i <= n; i++) {
		read(a[i].x); read(a[i].y);
		v[word[a[i].y]].push_back(i);
	}
	solve(0);
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	return 0;
}