Link
点我点我:-)
Background
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。
Descirption
打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。经阿狸研究发现,这个打字机是这样工作的:
·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
·按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。
·按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a aa ab 我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input & Output
input:
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
output:
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample
input1:
aPaPBbP
3
1 2
1 3
2 3
output1:
2
1
0
Hint
对于100%的数据,n<=100000,m<=100000, 第一行总长度<=100000。
Analysis
AC自动机好题!
首先建一棵AC自动机,然后按照它的fail边建一棵树(根据AC自动机的性质可知fail边组成的一定是棵树)
再对这棵树做一个Dfs遍历出来一个括号序列
我们定义一个串的编号为它在AC自动机中Tire的结尾节点的编号
考虑字符串x在字符串y中出现的次数,发现其为在fail树中x的子树中y到x经过的点数(含x, y)
那么可以在括号序列上维护一个树状数组,
每次加字符就是跳到新节点然后在它的左括号上加1,
删字符就是在左括号上减1然后跳回爸爸
询问就是处理以它为y的所有询问,枚举x,求在x的子树中(即x的左右括号间)和,就是x的子树中y到x经过的点数(含x, y)
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define pb push_back
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (200000+5)
int n, bn, dfn, fir[N], sec[N], ans[N], bh[N], fbh[N];
char r[N];
vector<int> G[N], q[N], id[N];
struct BIT{
int c[N];
int lowerbit(int x){return x&(-x);}
void Add(int now, int v){
while(now < N){
c[now] += v; now += lowerbit(now);
}
}
int Sum(int now){
int ret = 0;
while(now > 0){
ret += c[now]; now -= lowerbit(now);
}
return ret;
}
}bit;
struct AC_Automaton{
int Cnt, ch[N][30], fa[N], fail[N];
void Insert(){
int now = 0;
char nc;
For(nr, 1, n){
nc = r[nr];
if(nc == 'B'){now = fa[now]; continue;}
if(nc == 'P'){bh[now] = ++bn; fbh[bn] = now; continue;}
nc -= 'a';
if(!ch[now][nc]){ch[now][nc] = ++Cnt; fa[Cnt] = now;}
now = ch[now][nc];
}
}
void Getfail(){
queue<int> q;
int now, nxt, fs;
For(i, 0, 25){
now = ch[0][i];
if(now) G[0].pb(now), q.push(now), fail[now] = 0;
}
while(!q.empty()){
now = q.front(); q.pop();
For(i, 0, 25){
nxt = ch[now][i]; fs = ch[fail[now]][i];
if(!nxt) ch[now][i] = fs;
else{
fail[nxt] = fs;
G[fs].pb(nxt); q.push(nxt);
}
}
}
}
void Dfs(int now){
fir[now] = ++dfn;
For(i, 0, G[now].size()-1) Dfs(G[now][i]);
sec[now] = ++dfn;
}
void Solve(){
int o = 0, now, myid, myx;
char nc;
For(nr, 1, n){
nc = r[nr];
if(nc == 'B'){bit.Add(fir[o], -1); o = fa[o]; continue;}
if(nc != 'P'){o = ch[o][nc-'a']; bit.Add(fir[o], 1); continue;}
now = bh[o];
For(i, 0, q[now].size()-1){
myx = fbh[q[now][i]], myid = id[now][i];
ans[myid] = bit.Sum(sec[myx]) - bit.Sum(fir[myx]-1);
}
}
}
}ac;
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
int m, x, y;
scanf("%s%d", r+1, &m);
n = strlen(r+1);
ac.Insert(); ac.Getfail(); ac.Dfs(0);
For(i, 1, m){
scanf("%d%d", &x, &y);
q[y].pb(x); id[y].pb(i);
}
ac.Solve();
For(i, 1, m) printf("%d\n", ans[i]);
return 0;
}