【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 }

 

 

posted @ 2016-07-12 10:51  拦路雨偏似雪花  阅读(245)  评论(0编辑  收藏  举报