NOI 2011 阿貍的打字機 (AC自動機+dfs序+樹狀數組)
阿新 • • 發佈:2018-09-25
amp span 離線處理 oid 描述 can include nbsp 不能
題目大意:略(太長了不好描述) 良心LOJ傳送門
先對所有被打印的字符串建一顆Trie樹
觀察數據範圍,並不能每次打印都從頭到尾暴力建樹,而是每遍歷到一個字符就在Trie上插入這個字符,然後記錄每次打印後字符串最後一個字符在Trie樹上的位置
然後建立AC自動機,再建立Fail樹。註意還要另外存一下原來Trie樹的結構
Fail樹就是把Fail指針倒著跑,因為每個點只有一個Fail指針,所以最後所有Fail指針會形成一棵樹
Fail樹有一個神奇的性質,即父節點表示的字符串(從Trie樹的根節點一直到這個點所表示的字符串),一定是子節點表示的字符串的一個後綴
原問題是求x號字符串在y號字符串內出現的次數
問題可以轉化為,求x作為後綴,出現在y的所有前綴的次數
根據Fail樹的性質:父節點一定是子節點的後綴
那麽從Trie根節點到y結尾節點的所有點,都能分別表示y的一個前綴串
而如果x是某個串的後綴,那麽這個串一定在x的Fail樹的子樹內
如果對所有問題暴力匹配x,y又不可取
查詢y的所有前綴出現在x子樹內的次數,相當於在x子樹內求和,貌似可以用DFS序+樹狀數組優化
我們可以對問題進行離線處理,把問題掛在Trie樹上以y的結尾的位置上。然後跑出Fail樹DFS序。再在Tire樹上跑DFS,搜索到這個節點,就在它dfs序入棧的位置上+1,回溯過這個節點,就-1,這個操作其實是在表示所有可能的y的所有前綴
再回溯過這個節點之前,處理掛在這個點上所有的詢問。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define idx(x) x-‘a‘+1 6 #define lowbit(x) (x&(-x)) 7 #define N 100100 8 using namespace std; 9 10 char str[N]; 11 int n,m,len,num,cte,ctq,tot,dfn,qs;12 int use[N],pos[N],head[N],st[N],ed[N],s[N*2],hq[N]; 13 struct Edge{int to,nxt;}edge[N*2],ques[N]; 14 void ae(int u,int v){cte++;edge[cte].to=v,edge[cte].nxt=head[u],head[u]=cte;} 15 void aq(int u,int v){ctq++;ques[ctq].to=v,ques[ctq].nxt=hq[u],hq[u]=ctq;} 16 struct AC{ 17 int fa[N],son[N][28],ch[N][28],fail[N]; 18 int cre(int w,int ff){tot++;fa[tot]=ff;return tot;} 19 void Build() 20 { 21 int x=0; 22 for(int i=1;i<=len;i++) 23 { 24 if(‘a‘<=str[i]&&str[i]<=‘z‘){ 25 int w=idx(str[i]); 26 if(!ch[x][w]) ch[x][w]=son[x][w]=cre(w,x); 27 x=ch[x][w]; 28 }else if(str[i]==‘B‘){ 29 x=fa[x]; 30 }else pos[++num]=x; 31 } 32 } 33 void Fail() 34 { 35 queue<int>q; 36 for(int i=1;i<=26;i++) 37 if(ch[0][i]) q.push(ch[0][i]),ae(0,ch[0][i]); 38 while(!q.empty()) 39 { 40 int x=q.front();q.pop(); 41 for(int i=1;i<=26;i++) 42 if(ch[x][i]){ 43 fail[ch[x][i]]=ch[fail[x]][i]; 44 ae(fail[ch[x][i]],ch[x][i]); 45 q.push(ch[x][i]); 46 }else{ 47 ch[x][i]=ch[fail[x]][i]; 48 } 49 } 50 } 51 void dfs(int u) 52 { 53 st[u]=++dfn; 54 for(int j=head[u];j;j=edge[j].nxt){ 55 int v=edge[j].to; 56 dfs(v); 57 }ed[u]=++dfn; 58 } 59 void main() 60 { 61 Build(); 62 Fail(); 63 dfs(0); 64 } 65 }ac; 66 struct Ques{int x,y,id,ans;}q[N]; 67 void update(int x,int w){for(int i=x;i<=dfn;i+=lowbit(i))s[i]+=w;} 68 int query(int x){int ans=0;for(int i=x;i>0;i-=lowbit(i))ans+=s[i];return ans;} 69 void dfs_ans(int x) 70 { 71 update(st[x],1); 72 for(int i=1;i<=26;i++) 73 { 74 if(ac.son[x][i]) 75 dfs_ans(ac.son[x][i]); 76 } 77 for(int j=hq[x];j;j=ques[j].nxt){ 78 int v=ques[j].to; 79 q[v].ans=query(ed[pos[q[v].x]])-query(st[pos[q[v].x]]-1); 80 } 81 update(ed[x],-1); 82 } 83 84 int main() 85 { 86 scanf("%s",str+1); 87 len=strlen(str+1); 88 ac.main(); 89 int x,y; 90 scanf("%d",&m); 91 for(int i=1;i<=m;i++) 92 { 93 scanf("%d%d",&x,&y); 94 qs++,q[qs].x=x,q[qs].y=y,q[qs].id=i; 95 aq(pos[y],qs); 96 } 97 dfs_ans(0); 98 for(int i=1;i<=m;i++){printf("%d\n",q[i].ans);} 99 return 0; 100 }
NOI 2011 阿貍的打字機 (AC自動機+dfs序+樹狀數組)