bzoj 2434 AC自动机+树状数组
2434: [Noi2011]阿狸的打字机
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3493 Solved: 1909
[Submit][Status][Discuss]
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample Input
aPaPBbP
3
1 2
1 3
2 3
3
1 2
1 3
2 3
Sample Output
2
1
0
1
0
HINT
1<=N<=10^5
1<=M<=10^5
输入总长<=10^5
Source
代码:
//先把字符串直接建AC自动机,有删除操作'B'所以记一下父节点。val[i]数组记录第i个单词在字典树中的位置,我们知道x //在y中出现的次数就是y中有多少个fail指向x的结尾字符,我们把fail反向就可以看做一棵树,x在y中出现的位置一定是 //fail树的dfs序列中连续的一段,然后我们就可以用离线树状数组处理。处理出fail树的dfs序作(每个点有两个端点出和入) //为树状数组的点,按照y从小到大排序,然后按照字符串s的顺序加入字符时在该节点对应的dfs序的点+1,删除字符时-1, //当遇到y节点时就求出所有的与此y节点相关的询问这样按照y递增的顺序来求小的y就不会对大的y有影响了。 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int MAXN=100000; int m,node[MAXN+9][30],f[MAXN+9],val[MAXN+9],sz; int head[MAXN+9],tot,pre[MAXN+9],N,in[MAXN+9],out[MAXN+9],ans[MAXN+9],A[MAXN*4+9]; char s[MAXN+9]; struct Lu { int x,y,id; bool operator < (const Lu &p)const{ return y<p.y; } }L[MAXN+9]; struct Edge { int to,next; }edge[MAXN*4+9]; void init() { sz=tot=N=0; memset(node[0],0,sizeof(node[0])); f[0]=val[0]=0; memset(A,0,sizeof(A)); memset(head,-1,sizeof(head)); } void add_edge(int x,int y) { edge[tot].to=y; edge[tot].next=head[x]; head[x]=tot++; edge[tot].to=x; edge[tot].next=head[y]; head[y]=tot++; } void insert(char *s) { int len=strlen(s),rt=0,cnt=0; for(int i=0;i<len;i++){ if(s[i]=='B') { rt=pre[rt];continue; } else if(s[i]=='P') { val[++cnt]=rt;continue; } int id=s[i]-'a'; if(!node[rt][id]){ node[rt][id]=++sz; pre[sz]=rt; memset(node[sz],0,sizeof(node[sz])); val[sz]=0; } rt=node[rt][id]; } } void get_fail() { queue<int>q; for(int i=0;i<26;i++){ int u=node[0][i]; if(u) { q.push(u);f[u]=0; } } while(!q.empty()){ int rt=q.front();q.pop(); add_edge(rt,f[rt]); for(int i=0;i<26;i++){ int u=node[rt][i]; if(!u){ node[rt][i]=node[f[rt]][i]; continue; } q.push(u); f[u]=node[f[rt]][i]; //add_edge(u,f[u]); } } } void dfs(int x,int fa) { in[x]=++N; for(int i=head[x];i!=-1;i=edge[i].next){ int y=edge[i].to; if(y==fa) continue; dfs(y,x); } out[x]=N; } void add(int id,int c) { while(id<=MAXN){ A[id]+=c; id+=(id&-id); } } int query(int id) { int ss=0; while(id){ ss+=A[id]; id-=(id&-id); } return ss; } void solve(char *s) { int len=strlen(s),cnt=0,rt=0,j=0; for(int i=0;i<len;i++){ if(s[i]=='B'){ add(in[rt],-1); rt=pre[rt]; } else if(s[i]=='P'){ cnt++; while(j<m&&L[j].y==cnt){ ans[L[j].id]=query(out[val[L[j].x]])-query(in[val[L[j].x]]-1); j++; } }else{ rt=node[rt][s[i]-'a']; add(in[rt],1); } } } int main() { //freopen("in.txt","r",stdin); init(); scanf("%s",s); scanf("%d",&m); for(int i=0;i<m;i++){ scanf("%d%d",&L[i].x,&L[i].y); L[i].id=i; } sort(L,L+m); insert(s); get_fail(); dfs(0,-1); solve(s); for(int i=0;i<m;i++) printf("%d\n",ans[i]); return 0; }