[NOI2011] 阿狸的打字機
阿新 • • 發佈:2018-12-17
Description
給定\(N(N\leq 10^5)\)個串,\(m(m\leq 10^5)\)次詢問,每次詢問第\(x\)個串在第\(y\)個串中出現了幾次。
Solution
腦抽又調了一個小時。。。
考慮AC自動機fail指標的含義,如果 \(fail[x]=y\) 那說明以 \(y\) 結尾的串在 \(x\) 中出現了。
於是詢問 \(x\) 在 \(y\) 中出現了幾次實際上就是問trie樹上根到 \(y\) 的路徑有多少點的 \(fail\) 指標直接或間接指向了 \(x\) 的結尾節點。可以把 \(fail\) 指標當做樹邊拎出來建樹。
於是詢問變成在新的 \(fail\) 樹上以點 \(x\)
先dfs一遍 \(fail\) 樹求出dfs序轉化為序列上的問題。
直接在 \(fail\) 樹上做貌似不是很好做,我們回到原來的trie樹上,dfs 一遍整個trie樹。進入一個點就在序列上對應位置+1,退出一個點就在對應位置-1,這樣保證了只有當前路徑是有值的。如果碰到了單詞節點,就相當於我們遇到了詢問 \((x,y)\) 中的 \(y\),掃一下所有的 \(x\),查子樹和就行了。
Code
#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int M=8e5+5; const int N=1e5+5; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) vector< pii > v[N]; vector<int> js[M];char a[N]; int tot,head[M],ch[M][27],ans[N],fa[M]; int n,m,cnt,fs[N],fail[M],dfn[M],f[M],sze[M]; struct Edge{ int to,nxt; }edge[M]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } void add(int x){ while(x) f[x]++,x-=x&-x; } void clr(int x){ while(x) f[x]--,x-=x&-x; } int query(int x){ int now=0; while(x<=tot) now+=f[x],x+=x&-x; return now; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } void getfail(){ std::queue<int> q; for(int i=0;i<26;i++) if(ch[0][i]) q.push(ch[0][i]); while(q.size()){ int u=q.front();q.pop();add(fail[u],u); for(int i=0;i<26;i++){ if(ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],q.push(ch[u][i]); else ch[u][i]=ch[fail[u]][i]; } } } void dfs(int now){ sze[now]=1;dfn[now]=++tot; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; dfs(to);sze[now]+=sze[to]; } } void sdfs(int now){ add(dfn[now]); for(int x:js[now]) for(auto y:v[x]) ans[y.second]=query(dfn[fs[y.first]])-query(dfn[fs[y.first]]+sze[fs[y.first]]); for(int i=0;i<26;i++) if(ch[now][i]) sdfs(ch[now][i]); clr(dfn[now]); } signed main(){ scanf("%s",a+1); int len=strlen(a+1),now=0; for(int i=1;i<=len;i++){ if(a[i]>='a' and a[i]<='z'){ if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++tot,fa[tot]=now; now=ch[now][a[i]-'a']; } else if(a[i]=='B') now=fa[now]; else n++,js[now].pb(n),fs[n]=now; } tot=0;getfail();dfs(0);int cnts=0; memset(ch,0,sizeof ch);now=0; for(int i=1;i<=len;i++){ if(a[i]>='a' and a[i]<='z'){ if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++cnts; now=ch[now][a[i]-'a']; } else if(a[i]=='B') now=fa[now]; } m=getint(); for(int i=1;i<=m;i++){ int x=getint(),y=getint(); v[y].pb(mp(x,i)); } sdfs(0); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }