NowCoder 17633 - 阿貍的打字機
阿新 • • 發佈:2019-04-19
aca r++ 題目 har main 轉變 ext 轉化 iter ,問題又可以轉變為區間求和。
題目鏈接
題意:
??給定一個 \(n\) 個節點的 trie 樹,求 \(m\) 對節點作為結束位置代表的字符串之間的包含關系。
題解:
??對 trie 樹建 AC 自動機。根據 fail 指針的含義,我們發現,字符串 \(a\) 在字符串 \(b\) 中出現的次數等於 \(b\) 中 fail 指針直接或間接指向 \(a\) 的結束節點的節點數。
??所以,我們用 fail 指針的反向邊建出 fail 樹,則這個數字又可以轉化為 \(a\) 的結束節點的 fail 樹上屬於 \(b\) 的節點數量。
??如果根據 fail 樹的 dfs 序構造出 dfs 序數組,則 \(a\) 的結束節點的子樹一定是這個數組中連續的一段,對於每一個詢問 \(a,b\)
??因此,我們想到了一種離線的做法:我們以 trie 上的 dfs 序遍歷每個 \(b\),並維護 \(b\) 中的節點在這個數組上出現的情況,對於每一對 \(a,b\) 的詢問,就是對 \(a\) 的結束節點在 dfs 序數組上的開始位置和結束位置求區間和,可以用樹狀數組維護。
??下面是離線做法的代碼:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<map> using namespace std; const int maxn=1e5+5; struct BiTree{ int val[maxn]; BiTree(){ memset(val,0,sizeof(val)); } void initiate(){ memset(val,0,sizeof(val)); } inline int lowbit(int n){ return n&(-n); } void add(int n,int v){ int i; for (i=n;i<maxn;i+=lowbit(i)) val[i]+=v; } int query(int n){ int i,ans; ans=0; for (i=n;i>0;i-=lowbit(i)) ans+=val[i]; return ans; } } bi_tree; struct AcAuto{ const static int sigma=26; static int que[maxn]; int ch[maxn][sigma],fa[maxn],fail[maxn]; int pos[maxn]; int ndcnt,strcnt,root; int in[maxn],out[maxn],ope[maxn]; vector<int> next[maxn]; map<int,int> query[maxn]; AcAuto(){ ndcnt=0; strcnt=0; } int new_node(){ ++ndcnt; memset(ch[ndcnt],0,sizeof(ch[ndcnt])); fa[ndcnt]=0; fail[ndcnt]=0; return ndcnt; } void initiate(){ ndcnt=0; strcnt=0; root=new_node(); } inline int index(char ch){ return ch-'a'; } void add_str(char s[]){ int u=root; for (int i=0;s[i];++i){ if (s[i]=='B'){ u=fa[u]; continue; } if (s[i]=='P'){ pos[++strcnt]=u; continue; } int t=index(s[i]); if (!ch[u][t]){ int v=new_node(); ch[u][t]=v; fa[v]=u; } u=ch[u][t]; } } void build_fail(){ int l=0,r=0,u=root,v,w; for (int i=0;i<sigma;++i) if (ch[u][i]){ v=ch[u][i]; que[r++]=v; fail[v]=root; next[root].push_back(v); } while (l<r){ u=que[l++]; for (int i=0;i<sigma;++i) if (ch[u][i]){ v=ch[u][i]; que[r++]=v; w=fail[u]; while (w&&!ch[w][i]) w=fail[w]; fail[v]=w?ch[w][i]:root; next[fail[v]].push_back(v); } } } void fail_dfs(int u,int &time){ in[u]=++time; for (int i=0;i<next[u].size();++i){ int v=next[u][i]; fail_dfs(v,time); } out[u]=time; } void trie_dfs(int u){ bi_tree.add(in[u],1); map<int,int>::iterator it; for (it=query[u].begin();it!=query[u].end();it++) it->second=bi_tree.query(out[it->first]) -bi_tree.query(in[it->first]-1); for (int i=0;i<sigma;i++) if (ch[u][i]){ trie_dfs(ch[u][i]); } bi_tree.add(in[u],-1); } void build_dfs(){ int time=0; fail_dfs(root,time); trie_dfs(root); } } ac_auto; int AcAuto::que[maxn]; char s[maxn]; int x[maxn],y[maxn]; int main(){ int n; ac_auto.initiate(); scanf("%s",s); ac_auto.add_str(s); ac_auto.build_fail(); scanf("%d",&n); for (int i=0;i<n;i++){ scanf("%d%d",&x[i],&y[i]); ac_auto.query[ac_auto.pos[y[i]]][ac_auto.pos[x[i]]]=0; } ac_auto.build_dfs(); for (int i=0;i<n;i++){ printf("%d\n",ac_auto.query[ac_auto.pos[y[i]]][ac_auto.pos[x[i]]]); } return 0; }
??另外,還有一種對每一組詢問在線的做法:我們可以用可持久化線段樹來維護保存每一個字符串中的字符在 dfs 序數組中的出現情況,再對每一對 \(a,b\) 求區間和即可。但是,這種做法在原題中是不能通過的。(因為卡了內存,甚至連指針實現的 AC 自動機都會卡到)
??下面是這種寫法的代碼:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn=1e5+5; struct SegmentTree{ const static int maxd=maxn*20*2; int ls[maxd],rs[maxd]; int l[maxd],r[maxd],sum[maxd]; int ndcnt,opecnt,root[maxn]; SegmentTree(){ ndcnt=0; opecnt=0; } int new_node(){ ++ndcnt; l[ndcnt]=r[ndcnt]=0; ls[ndcnt]=rs[ndcnt]=0; sum[ndcnt]=0; return ndcnt; } void build_tree(int u,int ll,int rr){ l[u]=ll; r[u]=rr; if (ll==rr) return; int mm=(ll+rr)>>1; ls[u]=new_node(); build_tree(ls[u],ll,mm); rs[u]=new_node(); build_tree(rs[u],mm+1,rr); } void initiate(){ ndcnt=0; opecnt=0; root[0]=new_node(); build_tree(root[0],0,maxn); } void build_chain(int u,int v,int pos,int val){ ls[v]=ls[u]; rs[v]=rs[u]; l[v]=l[u]; r[v]=r[u]; sum[v]=sum[u]+val; if (l[v]==r[v]) return; int m=l[v]+r[v]>>1; if (pos<=m){ ls[v]=new_node(); build_chain(ls[u],ls[v],pos,val); } else{ rs[v]=new_node(); build_chain(rs[u],rs[v],pos,val); } } void add(int pos,int val){ root[++opecnt]=new_node(); build_chain(root[opecnt-1],root[opecnt],pos,val); } int query(int u,int ll,int rr){ if (l[u]>=ll&&r[u]<=rr) return sum[u]; if (l[u]>rr||r[u]<ll) return 0; return query(ls[u],ll,rr)+query(rs[u],ll,rr); } } segment_tree; struct AcAuto{ const static int sigma=26; static int que[maxn]; int ch[maxn][sigma],fa[maxn],fail[maxn]; int pos[maxn]; int ndcnt,strcnt,root; int in[maxn],out[maxn],ope[maxn]; vector<int> next[maxn]; AcAuto(){ ndcnt=0; strcnt=0; } int new_node(){ ++ndcnt; memset(ch[ndcnt],0,sizeof(ch[ndcnt])); fa[ndcnt]=0; fail[ndcnt]=0; return ndcnt; } void initiate(){ ndcnt=0; strcnt=0; root=new_node(); } inline int index(char ch){ return ch-'a'; } void add_str(char s[]){ int u=root; for (int i=0;s[i];++i){ if (s[i]=='B'){ u=fa[u]; continue; } if (s[i]=='P'){ pos[++strcnt]=u; continue; } int t=index(s[i]); if (!ch[u][t]){ int v=new_node(); ch[u][t]=v; fa[v]=u; } u=ch[u][t]; } } void build_fail(){ int l=0,r=0,u=root,v,w; for (int i=0;i<sigma;++i) if (ch[u][i]){ v=ch[u][i]; que[r++]=v; fail[v]=root; next[root].push_back(v); } while (l<r){ u=que[l++]; for (int i=0;i<sigma;++i) if (ch[u][i]){ v=ch[u][i]; que[r++]=v; w=fail[u]; while (w&&!ch[w][i]) w=fail[w]; fail[v]=w?ch[w][i]:root; next[fail[v]].push_back(v); } } } void trie_dfs(int u){ segment_tree.add(in[u],1); ope[u]=segment_tree.opecnt; for (int i=0;i<sigma;i++) if (ch[u][i]){ trie_dfs(ch[u][i]); } segment_tree.add(in[u],-1); } void fail_dfs(int u,int &time){ in[u]=++time; for (int i=0;i<next[u].size();++i){ int v=next[u][i]; fail_dfs(v,time); } out[u]=time; } void build_dfs(){ int time=0; fail_dfs(root,time); trie_dfs(root); } } ac_auto; int AcAuto::que[maxn]; char s[maxn]; int main(){ int n; int x,y; ac_auto.initiate(); scanf("%s",s); ac_auto.add_str(s); ac_auto.build_fail(); segment_tree.initiate(); ac_auto.build_dfs(); scanf("%d",&n); for (int i=0;i<n;i++){ scanf("%d%d",&x,&y); printf("%d\n",segment_tree.query( segment_tree.root[ac_auto.ope[ac_auto.pos[y]]], ac_auto.in[ac_auto.pos[x]], ac_auto.out[ac_auto.pos[x]] )); } return 0; }
NowCoder 17633 - 阿貍的打字機