[BZOJ2434][Noi2011]阿狸的打字机

[BZOJ2434][Noi2011]阿狸的打字机

试题描述

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

输入

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

输出

输出m行,其中第i行包含一个整数,表示第i个询问的答案。

输入示例

aPaPBbP
3
1 2
1 3
2 3

输出示例

2
1
0

数据规模及约定

1<=N<=10^5

1<=M<=10^5

输入总长<=10^5

题解

先构建一个AC自动机,建立好 Trie 树和 Fail 树。

不难想到一个暴力:对于每个询问(x, y),从 Trie 树中 y - root 的每个节点出发顺着 Fail 边走,能够到达 x 结点的路径条数即为答案。

不妨换个角度思考:对于询问(x, y),在 Fail 树中,以节点 x 为根的子树中满足“条件”的节点个数。一个节点满足“条件”当且仅当该节点在 Trie 树中的 y - root 上。

既然是处理子树问题,就可以用dfs序将其转化为序列问题了。

求一遍 Fail 树的dfs序,重新遍历一遍AC自动机,遇到 Trie 树上的节点 u 时将dfs序上位于 L[u] 的位置加 1,若是从节点 u 向上退格则将位于 L[u] 的位置减 1. 离线处理每个询问,在当前结点为 y 的时候,把所有(xi, y)的询问处理掉,其答案为区间 [ L[xi], R[xi] ] 的和。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 100010
#define maxs 26
int n, q, ans[maxn], pos[maxn], cnt;
char S[maxn];

int m, head[maxn], next[maxn], to[maxn];
void AddEdge(int a, int b) {
//	printf("%d -> %d\n", a, b);
	to[++m] = b; next[m] = head[a]; head[a] = m;
	return ;
}
int ToT = 1, f[maxn], fa[maxn], ch[maxn][maxs], Q[maxn], hd, tl;
void GetFail() {
	hd = tl = 0; f[1] = 1;
	for(int i = 0; i < maxs; i++) if(ch[1][i]) {
		int u = ch[1][i];
		f[u] = 1; Q[++tl] = u; AddEdge(1, u);
	}
	while(hd < tl) {
		int r = Q[++hd];
		for(int i = 0; i < maxs; i++) if(ch[r][i]) {
			int u = ch[r][i];
			int j = f[r];
			while(j > 1 && !ch[j][i]) j = f[j];
			f[u] = ch[j][i] ? ch[j][i] : 1;
			AddEdge(f[u], u);
			Q[++tl] = u;
		}
	}
	return ;
}
int tot, L[maxn], R[maxn];
void build(int u) {
	L[u] = ++tot;
	for(int e = head[u]; e; e = next[e]) build(to[e]);
	R[u] = tot;
	return ;
}

int headq[maxn], nextq[maxn], qx[maxn];

int C[maxn];
void add(int x, int v) { for(; x <= tot; x += x & -x) C[x] += v; return ; }
int sum(int x) { int res = 0; for(; x; x -= x & -x) res += C[x]; return res; }

int main() {
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	char tc = Getchar();
	while(!isalpha(tc)) tc = Getchar();
	int u = 1;
	while(isalpha(tc)) {
		if('a' <= tc && tc <= 'z') {
			int d = tc - 'a';
			if(!ch[u][d]) ch[u][d] = ++ToT, fa[ch[u][d]] = u;
			u = ch[u][d];
		}
		if(tc == 'P') pos[++cnt] = u;
		if(tc == 'B' && fa[u]) u = fa[u];
		S[++n] = tc;
		tc = Getchar();
	}
	
//	printf("%d\n", ToT);
//	for(int i = 1; i <= cnt; i++) printf("%d ", pos[i]); putchar('\n');
	GetFail(); build(1);
	q = read();
	for(int i = 1; i <= q; i++) {
		int x = pos[read()], y = pos[read()];
		nextq[i] = headq[y]; headq[y] = i; qx[i] = x;
	}
	u = 1;
	for(int i = 1; i <= n; i++) {
		if('a' <= S[i] && S[i] <= 'z') u = ch[u][S[i]-'a'], add(L[u], 1);
		if(S[i] == 'B' && fa[u]) add(L[u], -1), u = fa[u];
		if(S[i] == 'P')
			for(int i = headq[u]; i; i = nextq[i]) ans[i] = sum(R[qx[i]]) - sum(L[qx[i]]-1);
	}
	
	for(int i = 1; i <= q; i++) printf("%d\n", ans[i]);
	
	return 0;
}

 

posted @ 2016-04-30 14:11  xjr01  阅读(279)  评论(0编辑  收藏  举报