【bzoj2434-阿狸的打字机】AC自动机+fail树+优化
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23083
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Sample Input
aPaPBbP
3
1 2
1 3
2 3
Sample Output
2
1
0
题解:这道题做了好久。。哭泣。。。。。。。
主要是超时的问题。
fail树:按照AC自动机上fail的反向边建立的树。
如样例:
可以知道:failtree上,任意一点所代表的子串(即以trie上根到该点的串)是它在fail树上的子树任意一点的子串。
就是说,x是y的祖先,那么以y为结尾的串一直fail可以fail到x,就是x是y的子串。
那么现在可以得出一个简单粗暴的方法:
对于每个询问(x,y),求x在y上出现的次数,我们就可以在fail树上找到x串末尾的点A,然后询问y串在fail树上的每一个点是否是x的子树中的点,是就ans++。
但是明显,这个方法慢出天际了。。。。
优化:
(三颗星)离线。在fail树上用dfn给节点编号,则每一个点的子树在节点号上是连续的一段。在trie上走一遍,每到一个点x就在树状数组的dfn[x]位置+1,每离开一个点就在树状数组的dfn[x]位置-1。然后每当走到一个串的末尾,就询问每个对应要询问的x串,给fail树上它的子树求和,就是树状数组上getsum A~next[A]-1,这样就求出了y上多少个点为末尾可以fail到x,就是该询问(x,y)的答案。
后面就是我的错误点了:
(1)循环字符串的时候不要打for(int i=0;i<strlen(s);i++) ... 拿个变量存一下strlen(s)。。。超慢的。。
(2)做trietree的时候不用每次从头开始。。接着上一次的末尾就可以了。。快超多。。
(3)最后询问的时候不要把所有询问扫一遍。。用个first存y的边目录啊。。
血与泪啊。。
代码如下:
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<iostream>
5 #include<vector>
6 #include<queue>
7 #include<cmath>
8 using namespace std;
9
10 const int N=100010,S=26;
11 struct node{
12 int k,fa,fail,son[30];
13 }a[N];
14 struct edge{
15 int x,y,next,fa,ans;
16 }b[N],t[N];
17 char s[N];
18 int pre,m,tl,sl,num,cnt_s,len,cnt_dfn;
19 int last[N],c[N],sk[N],dfn[N],next[N],first[N],fir[N];
20 queue<int> q;
21
22 void clear(int x)
23 {
24 a[x].fa=a[x].k=a[x].fail=0;
25 memset(a[x].son,0,sizeof(a[x].son));
26 }
27
28 void buildAC()
29 {
30 while(!q.empty()) q.pop();
31 for(int i=1;i<=S;i++)
32 if(a[0].son[i]) q.push(a[0].son[i]);
33 while(!q.empty())
34 {
35 int x=q.front();q.pop();
36 int fail=a[x].fail;
37 for(int i=1;i<=S;i++)
38 {
39 if(a[x].son[i])
40 {
41 a[a[x].son[i]].fail=a[fail].son[i];
42 q.push(a[x].son[i]);
43 }
44 else a[x].son[i]=a[fail].son[i];
45 }
46 }
47 }
48
49 void ins(int x,int y,bool bk)
50 {
51 if(!bk)
52 {
53 b[++len].x=x;b[len].y=y;
54 b[len].next=first[x];first[x]=len;
55 }
56 else
57 {
58 t[++tl].x=x;t[tl].y=y;t[tl].ans=0;
59 t[tl].next=fir[x];fir[x]=tl;
60 }
61 }
62
63 void dfs(int x)
64 {
65 dfn[x]=++cnt_dfn;
66 for(int i=first[x];i;i=b[i].next)
67 {
68 int y=b[i].y;
69 dfs(y);
70 }
71 next[x]=cnt_dfn;
72 }
73
74 void build_fail_tree()
75 {
76 for(int i=1;i<=num;i++) ins(a[i].fail,i,0);
77 dfs(0);
78 }
79
80 int lowbit(int x){return x&(-x);}
81 void add(int x,int d){for(int i=x;i<=N-10;i+=lowbit(i)) c[i]+=d;}
82 int getsum(int x)
83 {
84 int ans=0;
85 for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i];
86 return ans;
87 }
88
89 int main()
90 {
91 freopen("a.in","r",stdin);
92 freopen("me.out","w",stdout);
93 num=0;cnt_s=0;cnt_dfn=0;len=0;pre=0;tl=0;clear(0);
94 memset(fir,0,sizeof(fir));
95 memset(first,0,sizeof(first));
96 memset(c,0,sizeof(c));
97 scanf("%s",s);
98 //build trie tree
99 int strl=strlen(s),now=0;
100 for(int i=0;i<strl;i++)
101 {
102 if(s[i]=='P')
103 {
104 a[now].k=1;
105 last[++cnt_s]=now;
106 }
107 else if(s[i]=='B') now=a[now].fa;
108 else
109 {
110 int ind=s[i]-'a'+1;
111 if(!a[now].son[ind])
112 {
113 num++;clear(num);
114 a[now].son[ind]=num;
115 a[num].fa=now;
116 }
117 now=a[now].son[ind];
118 }
119 }
120 //build ac
121 buildAC();
122 //build fail tree
123 build_fail_tree();
124 scanf("%d",&m);
125 for(int i=1;i<=m;i++)
126 {
127 int x,y;
128 scanf("%d%d",&x,&y);
129 ins(y,x,1);
130 }
131 //find
132 now=0,cnt_s=0;
133 for(int i=0;i<strl;i++)
134 {
135 if(s[i]=='B')
136 {
137 add(dfn[now],-1);
138 now=a[now].fa;
139 }
140 else if(s[i]=='P')
141 {
142 cnt_s++;
143 for(int j=fir[cnt_s];j;j=t[j].next)
144 {
145 int st=dfn[last[t[j].y]],ed=next[last[t[j].y]];
146 t[j].ans=getsum(ed)-getsum(st-1);
147 }
148 }
149 else
150 {
151 int ind=s[i]-'a'+1;
152 now=a[now].son[ind];
153 add(dfn[now],1);
154 }
155 }
156 for(int i=1;i<=m;i++)
157 printf("%d\n",t[i].ans);
158 return 0;
159 }