uni-app-元件通訊
阿新 • • 發佈:2020-09-03
Solution [NOI2011]阿狸的打字機
題目大意:給定一顆\(Trie\)樹,多次詢問\(x\)號節點代表的字串在\(y\)號節點代表的字串中出現了多少次
AC自動機
分析:首先我們回顧一下\(AC\)自動機上匹配的過程
我們沿著文字串在\(Trie\)圖上面走(假設已經用類似路徑壓縮的辦法,把\(Trie\)樹給補全,當然此題直接從根走到\(y\)即可),走到每個節點,一直沿著\(fail\)跳,跳到的節點代表能匹配(當前節點代表字首的字尾)的字首(有點繞加了個括號)。
跳到的節點如果是某個字串的結尾,那麼就匹配上了。
那麼這題的暴力演算法就是從根走到\(y\),路徑上每個節點跳\(fail\)
顯然複雜度無法承受,我們考慮從\(x\)的角度來考慮
將\(fail\)邊反向就可以得到\(fail\)樹,實際上我們只需要查詢\(x\)為根的子樹內,有多少個節點在根到\(y\)的鏈
可以線上做,也可以離線,利用\(dfs\)序來統計
我們將詢問\((x,y)\)儲存在\(y\)內,進行一次\(dfs\),在新進入一個節點時在它\(dfs\)序位置\(+1\),離開時撤銷。對於它所有的詢問,直接查詢子樹和即可
樹狀陣列可以維護
#include <cstdio> #include <cstring> #include <vector> #include <queue> using namespace std; const int maxn = 1e5 + 100; struct mpair{int first,second;}; inline mpair make_pair(int x,int y){return mpair{x,y};} extern int ans[maxn]; namespace bit{ int f[maxn]; inline int lowbit(int x){return x & (-x);} inline void add(int pos,int x){ while(pos < maxn){ f[pos] += x; pos += lowbit(pos); } } inline int query(int pos){ int res = 0; while(pos){ res += f[pos]; pos -= lowbit(pos); } return res; } inline int query(int a,int b){return query(b) - query(a - 1);} } namespace graph{ vector<int> G[maxn]; inline void addedge(int from,int to){G[from].push_back(to);} int siz[maxn],dfn[maxn],dfs_tot; inline void dfs(int u){ dfn[u] = ++dfs_tot; siz[u] = 1; for(int v : G[u])dfs(v),siz[u] += siz[v]; } } namespace ac{ const int siz = 26; int ch[maxn][siz],chhis[maxn][siz],faz[maxn],fail[maxn],tot; vector<mpair> query[maxn]; inline int idx(char c){return c - 'a';} inline void build(){ memcpy(chhis,ch,sizeof(ch)); queue<int> Q; for(int c = 0;c < siz;c++) if(ch[0][c])Q.push(ch[0][c]); while(!Q.empty()){ int u = Q.front();Q.pop(); for(int c = 0;c < siz;c++) if(ch[u][c])fail[ch[u][c]] = ch[fail[u]][c],Q.push(ch[u][c]); else ch[u][c] = ch[fail[u]][c]; } for(int u = 1;u <= tot;u++)graph::addedge(fail[u],u); } inline void dfs(int u){ // printf("%d\n",u); if(u)bit::add(graph::dfn[u],1); if(u)for(auto p : query[u]){ // printf("") ans[p.first] = bit::query(graph::dfn[p.second],graph::dfn[p.second] + graph::siz[p.second] - 1); } for(int c = 0;c < siz;c++) if(chhis[u][c])dfs(chhis[u][c]); if(u)bit::add(graph::dfn[u],-1); } } char str[maxn]; int m,now,pos[maxn],ans[maxn],print; int main(){ #ifdef LOCAL freopen("type3.in","r",stdin); #endif scanf("%s",str + 1); for(int i = 1;str[i];i++){ if(str[i] == 'B')now = ac::faz[now]; else if(str[i] == 'P')pos[++print] = now; else{ int c = ac::idx(str[i]); if(!ac::ch[now][c])ac::ch[now][c] = ++ac::tot,ac::faz[ac::tot] = now; now = ac::ch[now][c]; } } ac::build(); graph::dfs(0); scanf("%d",&m); for(int x,y,i = 1;i <= m;i++){ scanf("%d %d",&x,&y); ac::query[pos[y]].push_back(make_pair(i,pos[x])); } ac::dfs(0); for(int i = 1;i <= m;i++)printf("%d\n",ans[i]); return 0; }