AC自动机:BZOJ 2434 阿狸的打字机
2434: [Noi2011]阿狸的打字机
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1834 Solved: 1053
[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
这题我很久以前见到过,当时没敢动手,现在学了AC自动机后,果断拿来练手了。
题意很简单,可以离线。
这里在建完fail树后须将fail边反向,建成一棵fail树,如果能A到B有条边,那么代表trie树上到A的字符串是到B的字符串的后缀,这时来一遍DFS,记录每一个点的时间戳,即一个时间段,be[i]指i点自己的编号,也指其开始,en[i]指其结束。
对于一个节点,它在fail树上的子树中,所有节点的fail都直接或间接的连向它,指它是那些的子串,被他们包含,而用时间戳可以迅速知道一个点是否是其子树。
之后用离线做法处理答案,用bit维护。
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #include <queue>
5 #include <algorithm>
6 using namespace std;
7 const int maxn=1e5+10;
8 char S[maxn];
9 int Query[maxn][2],ans[maxn],cntQ;
10 struct A_Cautomation{
11 int ch[maxn][27],fail[maxn],end[maxn],fa[maxn],ID[maxn],cnt,root,cont;
12 int fir[maxn],nxt[maxn],to[maxn],cot;
13 int be[maxn],en[maxn],sjc;
14 void Init()
15 {
16 memset(ch,0,sizeof(ch));
17 memset(fail,0,sizeof(fail));
18 memset(end,0,sizeof(end));
19 sjc=cot=cnt=cont=root=0;
20 }
21 void Insert()
22 {
23 scanf("%s",S);
24 int len=strlen(S),node=root;
25 for(int i=0;i<len;i++)
26 {
27 if(S[i]>='a'&&S[i]<='z'){
28 if(ch[node][S[i]-'`']){
29 node=ch[node][S[i]-'`'];
30 }
31 else{
32 fa[++cnt]=node;
33 node=ch[node][S[i]-'`']=cnt;
34 }
35 }
36 else{
37 if(S[i]=='B'){
38 node=fa[node];
39 }
40 else{
41 end[node]=++cont;
42 ID[cont]=node;
43 }
44 }
45 }
46 }
47 void Build()
48 {
49 queue<int>q;
50 for(int i=1;i<=26;i++){
51 if(ch[root][i]){
52 fail[ch[root][i]]=root;
53 q.push(ch[root][i]);
54 }
55 else
56 ch[root][i]=root;
57 }
58 while(!q.empty())
59 {
60 int node=q.front();q.pop();
61 for(int i=1;i<=26;i++){
62 if(ch[node][i]){
63 fail[ch[node][i]]=ch[fail[node]][i];
64 q.push(ch[node][i]);
65 }
66 else{
67 ch[node][i]=ch[fail[node]][i];
68 }
69 }
70 }
71 }
72
73 void addedge(int a,int b)
74 {nxt[++cot]=fir[a];fir[a]=cot;to[cot]=b;}
75
76 void DFS(int node)
77 {
78 be[node]=++sjc;
79 for(int i=fir[node];i;i=nxt[i])
80 DFS(to[i]);
81 en[node]=sjc;
82 }
83
84 void BuildTree()
85 {
86 for(int i=1;i<=cnt;i++)
87 addedge(fail[i],i);
88
89 DFS(root);
90 }
91
92 int bit[maxn];
93 void change(int k,int x)
94 {
95 while(k<=100000)
96 {
97 bit[k]+=x;
98 k+=k&(-k);
99 }
100 }
101
102 int Quer(int k)
103 {
104 int ret=0;
105 while(k)
106 {
107 ret+=bit[k];
108 k-=k&(-k);
109 }
110 return ret;
111 }
112
113 void Solve()
114 {
115 memset(fir,0,sizeof(fir));cot=0;
116 memset(bit,0,sizeof(bit));
117 for(int i=1;i<=cntQ;i++)
118 Query[i][0]=ID[Query[i][0]],
119 Query[i][1]=ID[Query[i][1]],
120 addedge(Query[i][1],Query[i][0]);
121
122 int len=strlen(S),node=root;
123 for(int i=0;i<len;i++)
124 {
125 if(S[i]>='a'&&S[i]<='z'){
126 node=ch[node][S[i]-'`'];
127 change(be[node],1);
128 }
129 else{
130 if(S[i]=='B'){
131 change(be[node],-1);
132 node=fa[node];
133 }
134 else{
135 for(int i=fir[node];i;i=nxt[i]){
136 ans[i]=Quer(en[to[i]])-Quer(be[to[i]]-1);
137 }
138 }
139 }
140 }
141
142 for(int i=1;i<=cntQ;i++)
143 printf("%d\n",ans[i]);
144 }
145 }AC;
146
147 int main()
148 {
149 AC.Init();
150 AC.Insert();
151 AC.Build();
152 AC.BuildTree();
153
154 int Q;
155 scanf("%d",&Q);
156 while(Q--)
157 scanf("%d%d",&Query[cntQ][0],&Query[++cntQ][1]);
158
159 AC.Solve();
160 return 0;
161 }
尽最大的努力,做最好的自己!