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