BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

题目这么说的:

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:

  1. 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
  2. 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
  3. 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

  • 把AC自动机的结点作为树结点,将fail指针的方向取反设为树边,这样除了AC自动机头结点外每个结点都有且仅有一条进入它的边,这样就得到一棵叫fail树的树。
  • 在AC自动机中如果y的fail指针指向x,可知x表示的字符串前缀是y结点表示的字符串前缀的后缀;因此可以知道在fail树中x为根的子树的所有结点表示的字符串前缀都包含x结点所表示的字符串前缀。
  • 对于,询问一个字符串x在另一个字符串y出现了多少次,就可以这样:在fail树中标记字符串y的所有前缀结点,然后看x为根的子树有多少个被标记结点就是答案,这个可以用DFS序+线段树实现。
  • 而对于这题的具体实现:
    1. 首先可以顺着输入的字符串序列B往父亲走,P标记这样建立AC自动机
    2. 然后把查询离线处理,用邻接表记录每一组查询<x,y>中每个y有哪几个x
    3. 最后再顺着输入的字符串序列,遇到B和小写字符更新线段树,遇到P去查询出当前y的所有x的答案
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<algorithm>
  5 using namespace std;
  6 #define MAXN 111111
  7 
  8 struct Edge{
  9     int v,next;
 10 }edge[MAXN];
 11 int NE,head[MAXN];
 12 void addEdge(int u,int v){
 13     edge[NE].v=v; edge[NE].next=head[u];
 14     head[u]=NE++;
 15 }
 16 
 17 int tn,ch[MAXN][26],fa[MAXN],flag[MAXN],fail[MAXN];
 18 int belong[MAXN],bn,rbelong[MAXN];
 19 char str[111111];
 20 void insert(){
 21     int x=0;
 22     for(int i=0; str[i]; ++i){
 23         if(str[i]=='B'){
 24             x=fa[x];
 25         }else if(str[i]=='P'){
 26             flag[x]=1;
 27             belong[x]=++bn;
 28             rbelong[bn]=x;
 29         }else{
 30             int y=str[i]-'a';
 31             if(ch[x][y]==0) ch[x][y]=++tn,fa[ch[x][y]]=x;
 32             x=ch[x][y];
 33         }
 34     }
 35 }
 36 void getfail(){
 37     queue<int> que;
 38     for(int i=0; i<26; ++i){
 39         if(ch[0][i]){
 40             que.push(ch[0][i]);
 41             addEdge(0,ch[0][i]);
 42         }
 43     }
 44     while(!que.empty()){
 45         int x=que.front(); que.pop();
 46         for(int i=0; i<26; ++i){
 47             if(ch[x][i]){
 48                 que.push(ch[x][i]);
 49                 fail[ch[x][i]]=ch[fail[x]][i];
 50                 addEdge(fail[ch[x][i]],ch[x][i]);
 51             }else ch[x][i]=ch[fail[x]][i];
 52         }
 53     }
 54 }
 55 
 56 int dfn,l[MAXN],r[MAXN],par[MAXN];
 57 void dfs(int u){
 58     l[u]=++dfn;
 59     for(int i=head[u]; i!=-1; i=edge[i].next){
 60         int v=edge[i].v;
 61         par[v]=u;
 62         dfs(v);
 63     }
 64     r[u]=dfn;
 65 }
 66 
 67 int tree[MAXN<<2],N,x,y;
 68 void update(int i,int j,int k){
 69     if(i==j){
 70         tree[k]+=y;
 71         return;
 72     }
 73     int mid=i+j>>1;
 74     if(x<=mid) update(i,mid,k<<1);
 75     else update(mid+1,j,k<<1|1);
 76     tree[k]=tree[k<<1]+tree[k<<1|1];
 77 }
 78 int query(int i,int j,int k){
 79     if(x<=i && j<=y){
 80         return tree[k];
 81     }
 82     int mid=i+j>>1,res=0;
 83     if(x<=mid) res+=query(i,mid,k<<1);
 84     if(y>mid) res+=query(mid+1,j,k<<1|1);
 85     return res;
 86 }
 87 
 88 struct Query{
 89     int x,y,ans,next;
 90 }que[MAXN];
 91 int qhead[MAXN],qNE,order[MAXN];
 92 void addQuery(int x,int y){
 93     que[qNE].x=x; que[qNE].y=y; que[qNE].next=qhead[y];
 94     qhead[y]=qNE++;
 95 }
 96 
 97 void doit(){
 98     int now=0;
 99     for(int i=0; str[i]; ++i){
100         if(str[i]=='B'){
101             x=l[now]; y=-1;
102             update(1,N,1);
103             now=fa[now];
104         }else if(str[i]=='P'){
105             for(int j=qhead[belong[now]]; j!=-1; j=que[j].next){
106                 x=l[rbelong[que[j].x]]; y=r[rbelong[que[j].x]];
107                 que[j].ans=query(1,N,1);
108             }
109         }else{
110             now=ch[now][str[i]-'a'];
111             x=l[now]; y=1;
112             update(1,N,1);
113         }
114     }
115 }
116 int main(){
117     scanf("%s",str);
118     insert();
119     memset(head,-1,sizeof(head));
120     getfail();
121     dfs(0);
122     for(N=1; N<dfn; N<<=1);
123     memset(qhead,-1,sizeof(qhead));
124     int m,a,b;
125     scanf("%d",&m);
126     for(int i=0; i<m; ++i){
127         scanf("%d%d",&a,&b);
128         order[i]=qNE;
129         addQuery(a,b);
130     }
131     doit();
132     for(int i=0; i<m; ++i){
133         printf("%d\n",que[order[i]].ans);
134     }
135     return 0;
136 }

 

posted @ 2016-04-19 10:41  WABoss  阅读(281)  评论(0编辑  收藏  举报