【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample Input
aPaPBbP
3
1 2
1 3
2 3
Sample Output
2
1
0HINT
1<=N<=10^5
1<=M<=10^5
输入总长<=10^5
【分析】
- 这道题是用AC自动机里的fail指针连成的树做的。
- 用到fail树。
- 首先有一个朴素算法就是找到第y个单词在trie树上的路径然后沿着每一个点的fail指针走,如果找到x就加1(想想fail指针建立的过程)。
- 由此可以运用逆向思维,以x为根的子树沿着fail指针倒着走能找到多少个y路径上的点就说明x在y上出现过几次。每次都dfs找一遍,用树状数组维护,这样可以得到70分。
- 同时这是一个离线算法,一遍dfs,遇到一个结束标记,就做一下这个串的询问,插一个点在树状数组+1,离开这个点时-1。
- 对于树上每个点只插入一次,时间复杂度就得到了保证。
有一个很详细的题解:http://blog.csdn.net/huzecong/article/details/7769988
代码如下:(丑)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 100100 9 #define Maxl 100010 10 11 char s[Maxl]; 12 int p[Maxn]; 13 14 struct hp 15 { 16 int x,y,next,id,ans; 17 }qy[Maxn];int al; 18 19 struct node 20 { 21 int son[30],cnt,fail; 22 int num,rt,fa,st; 23 }t[Maxn];int tot; 24 25 int first[Maxn],c[Maxn]; 26 27 bool cmp(hp x,hp y) {return p[x.y]<p[y.y];} 28 bool cmp2(hp x,hp y) {return x.id<y.id;} 29 30 void ins(int x,int y) 31 { 32 qy[++al].x=x;qy[al].y=y; 33 qy[al].next=first[x];first[x]=al; 34 } 35 36 void upd(int x,int f) 37 { 38 t[x].cnt=0;t[x].fa=f;t[x].st=0; 39 memset(t[x].son,0,sizeof(t[x].son)); 40 } 41 42 void read_trie() 43 { 44 scanf("%s",s+1); 45 int len=strlen(s+1); 46 memset(p,0,sizeof(p)); 47 int now=0;int ql=0;tot=0; 48 for(int j=1;j<=len;j++) 49 { 50 if(s[j]>='a'&&s[j]<='z') 51 { 52 int ind=s[j]-'a'+1; 53 if(!t[now].son[ind]) 54 t[now].son[ind]=++tot,upd(tot,now); 55 now=t[now].son[ind]; 56 if(j==len) t[now].cnt++; 57 } 58 else if(s[j]=='B') now=t[now].fa; 59 else p[++ql]=now; 60 } 61 } 62 63 queue<int > q; 64 void build_AC() 65 { 66 int i,j,x,y; 67 while(!q.empty()) q.pop(); 68 q.push(0); 69 while(!q.empty()) 70 { 71 x=q.front(); 72 y=t[x].fail; 73 for(j=1;j<=26;j++) 74 { 75 if(t[x].son[j]) 76 { 77 q.push(t[x].son[j]); 78 t[t[x].son[j]].fail=x?t[y].son[j]:x; 79 ins(t[t[x].son[j]].fail,t[x].son[j]); 80 } 81 else t[x].son[j]=t[y].son[j]; 82 } 83 q.pop(); 84 } 85 } 86 87 void dfs(int x) 88 { 89 t[x].num=++al;t[x].rt=t[x].num; 90 for(int i=first[x];i;i=qy[i].next) 91 dfs(qy[i].y),t[x].rt=t[qy[i].y].rt; 92 } 93 94 void add(int x,int y) 95 { 96 for(int i=x;i<=tot+1;i+=i&(-i)) 97 c[i]+=y; 98 } 99 100 int getsum(int x) 101 { 102 int ans=0; 103 for(int i=x;i>=1;i-=i&(-i)) 104 ans+=c[i]; 105 return ans; 106 } 107 108 void dfs2(int x) 109 { 110 add(t[x].num,1); 111 if(t[x].st!=0) 112 { 113 for(int i=t[x].st;;i++) 114 { 115 if(p[qy[i].y]!=x) break; 116 qy[i].ans=getsum(t[p[qy[i].x]].rt)-getsum(t[p[qy[i].x]].num-1); 117 } 118 } 119 for(int i=1;i<=26;i++) if(t[x].son[i]&&t[t[x].son[i]].fa==x) 120 { 121 dfs2(t[x].son[i]); 122 } 123 add(t[x].num,-1); 124 } 125 126 void init() 127 { 128 memset(first,0,sizeof(first)); 129 al=0; 130 read_trie(); 131 build_AC();al=0; 132 dfs(0); 133 int m; 134 scanf("%d",&m); 135 for(int i=1;i<=m;i++) {scanf("%d%d",&qy[i].x,&qy[i].y);qy[i].id=i;} 136 sort(qy+1,qy+1+m,cmp); 137 qy[0].y=0; 138 for(int i=1;i<=m;i++) 139 if(p[qy[i].y]!=p[qy[i-1].y]) t[p[qy[i].y]].st=i; 140 else t[p[qy[i].y]].st=t[p[qy[i-1].y]].st; 141 memset(c,0,sizeof(c)); 142 dfs2(0); 143 sort(qy+1,qy+1+m,cmp2); 144 for(int i=1;i<=m;i++) printf("%d\n",qy[i].ans); 145 } 146 147 int main() 148 { 149 init(); 150 return 0; 151 }
2016-06-16 16:55:20