【dfs序+AC自动机+树状数组】BZOJ2434-[Noi2011]阿狸的打字机

【题目大意】

输入一个字符串,其中:(1)a..z:在字符串末尾添加当前字符(2)P:输出当前字符串(3)B:从当前字符串末尾删去一个字符。

给出m组查询,输出第i个输出的字符串在第j个输出的字符串内出现了几次。

【思路】

卡了好久,写完不想调试,调试完不想提交,期间颓颓颓地弄了下博客的界面,弄成了粉嫩少女风(划掉)。结果提交完1A有点迷醉迷醉的……

首先我们要借用BZOJ3172的结论:←戳这里,这个结论66666,是本次解题的关键。

“建立AC自动机,对于路径上的每一个点sum++,表示出现的次数。fail指针指向的后缀,如果从fail指针指向的点开始方向建立fail树,其子树的sum之和就等于以它作为后缀的串的总数,相当于它在文章中出现的个数。”

显然我们要把m组询问(i,j)调换位置,同一组j,对i建立一个链表存放。为什么要这么做呢?先继续往下看。

首先,我们建立一个AC自动机,并建立fail树。这里和一般的建立有点区别,因为有删除操作,可以用一个栈来维护当前位于Trie的位置,如果B删除就弹出栈顶,如果为字母就建立新节点并压入栈中,如果是P就在当前位置打上ed的标记,表示这是第ed次打印的字符终止位置。

由于树中节点和子树的dfs序是连续的,我们跑一次dfs记录下fail树上每个点的dfs序,记录下第i个输出字符串,它的末尾节点及其子树在dfs序中的start和end位置。

最后,重新跑一次Trie,和先前一样用栈维护。如果遇到B,就将当前节点-1,弹出栈顶;遇到字母就将当前节点+1;如果遇到P,说明现在恰巧输出了第j个字符串,枚举每一个查询中的i,查询结果=当前i对应的节点和它子树之和。

没错,这是一个单点修改,区间求和,可以用树状数组来维护一下,每个节点在树状数组中的位置就等于它的dfs序。那么当前i对应的节点及其子树之和=sum[start[i]..end[i]],搞定!

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<queue>
  7 #define lnum 26
  8 using namespace std;
  9 const int MAXN=100000+50;
 10 int  ppos[MAXN];//第i个P对应的栈顶位置编号
 11 char str[MAXN];
 12 int m,len,step,cnt=0,e[MAXN],start[MAXN],end[MAXN],ans[MAXN];
 13 struct ACauto
 14 {
 15     ACauto* next[lnum];
 16     ACauto* fail;
 17     int id,ed,order; 
 18     ACauto()
 19     {
 20         for (int i=0;i<lnum;i++) next[i]=NULL;
 21         fail=NULL;
 22         id=++cnt;
 23     }
 24 };
 25 struct node
 26 {
 27     int to,id;
 28 }; 
 29 ACauto* rt=new ACauto();
 30 ACauto* p[MAXN];//编号为i的节点指向的位置
 31 vector<node> Q[MAXN];
 32 vector<int> E[MAXN];
 33 
 34 void addedge(int u,int v)
 35 {
 36     E[u].push_back(v);
 37 }
 38 
 39 int lowbit(int x)
 40 {
 41     return (x&(-x));
 42 }
 43 
 44 void modify(int p,int x)
 45 {
 46     int i=p;
 47     while (i<=len)
 48     {
 49         e[i]+=x;
 50         i+=lowbit(i);
 51     }
 52 }
 53 
 54 int sum(int p)
 55 {
 56     int s=0;
 57     int i=p;
 58     while (i>0)
 59     {
 60         s+=e[i];
 61         i-=lowbit(i);
 62     }
 63     return s;
 64 }
 65 
 66 void init()
 67 {
 68     scanf("%s",str);
 69     len=strlen(str);
 70     int top=0,n=0;
 71     ACauto* stack[MAXN];
 72     stack[++top]=p[1]=rt;//不要忘记给p[1]赋值 
 73     for (int i=0;i<len;i++)
 74     {
 75         if (str[i]=='P') 
 76         {
 77             stack[top]->ed=++n;
 78             ppos[n]=stack[top]->id;
 79         }
 80             else if (str[i]=='B') top--;
 81                 else
 82                 {
 83                     int index=str[i]-'a';
 84                     if (stack[top]->next[index]==NULL) 
 85                     {
 86                         stack[top]->next[index]=new ACauto();
 87                         p[cnt]=stack[top]->next[index];
 88                     }
 89                     stack[++top]=stack[top-1]->next[index];
 90                 }
 91     }
 92     
 93     scanf("%d",&m);
 94     for (int i=0;i<m;i++)
 95     {
 96         int x,y;
 97         scanf("%d%d",&x,&y);
 98         Q[y].push_back((node){ppos[x],i});
 99     }
100 } 
101 
102 void buildfail()
103 {
104     queue<ACauto*> que;
105     que.push(rt);
106     while (!que.empty())
107     {
108         ACauto* head=que.front();que.pop();
109         for (int i=0;i<lnum;i++)
110         {
111             if (head->next[i]!=NULL)
112             {
113                 if (head==rt)
114                 {
115                     head->next[i]->fail=rt;
116                     addedge(rt->id,head->next[i]->id);
117                 }
118                 else
119                 {
120                     ACauto* tmp=head->fail;
121                     while (tmp!=NULL)
122                     {
123                         if (tmp->next[i]!=NULL)
124                         {
125                             head->next[i]->fail=tmp->next[i];
126                             addedge(tmp->next[i]->id,head->next[i]->id);
127                             break;
128                         }
129                         else tmp=tmp->fail;
130                     }
131                     if (tmp==NULL) 
132                     {
133                         head->next[i]->fail=rt;
134                         addedge(rt->id,head->next[i]->id);
135                     }
136                 }
137                 que.push(head->next[i]);
138             }
139         }
140     }
141 }
142 
143 void dfs(int pos)
144 {
145     ACauto* tmp=p[pos];
146     tmp->order=++step;
147     if (tmp->ed) start[tmp->ed]=step;
148     for (int i=0;i<E[pos].size();i++) dfs(E[pos][i]);
149     if (tmp->ed) end[tmp->ed]=step;
150 }
151 
152 void query()
153 {
154     memset(e,0,sizeof(e));
155     int top=0,n=0;
156     ACauto* stack[MAXN];
157     stack[++top]=rt;
158     for (int i=0;i<len;i++)
159     {
160         if (str[i]=='P')
161         {
162             n++;
163             for (int i=0;i<Q[n].size();i++)
164             {
165                 ACauto* to=p[Q[n][i].to];
166                 ans[Q[n][i].id]=sum(end[to->ed])-sum(start[to->ed]-1);
167             }
168         }
169         else if (str[i]=='B')
170         {
171             modify(stack[top]->order,-1);
172             top--;
173         }
174         else
175         {
176             int index=str[i]-'a';
177             stack[++top]=stack[top-1]->next[index];
178             modify(stack[top]->order,1);
179         }
180     }
181 }
182 
183 void printans()
184 {
185     for (int i=0;i<m;i++) printf("%d\n",ans[i]);
186 }
187 
188 int main()
189 {
190     init();
191     buildfail();
192     dfs(rt->id);
193     query();
194     printans();
195     return 0;
196 }

 

posted @ 2016-07-26 10:58  iiyiyi  阅读(231)  评论(0编辑  收藏  举报