「ICPC World Finals 2019 何以伊名始
阿新 • • 發佈:2020-09-18
「ICPC World Finals 2019 何以伊名始
本題現已知四種做法,如果不會後綴系列結構可以直接看Solution4
設初始樹大小和查詢總長均為\(O(n)\)
Solution1
由於查詢只有1e6,因此出現的不同查詢串長度最多\(O(\sqrt {10^6})=1500\)種
考慮對於每一種做一次dfs,在\(\text{Hash Table}\)中查詢,複雜度為\(O(n\sqrt n)\)
Solution2
將樹上節點字尾排序,然後每次插入需要詢問的字元就二分字尾區間
預處理複雜度為\(O(n\log n)\),查詢涉及二分和倍增,複雜度為\(O(n\log ^2 n)\)
Solution3
給定的樹看做trie樹,可以對於trie樹建廣義字尾自動機,然後倒著讓詢問串去匹配,一旦失配答案為0,
需要預處理\(link\)樹的子樹和,時間複雜度為\(O(n)\),空間複雜度為\(O(n|\Sigma|)\)
Solution4
將詢問的串倒著插入,構建AC自動機
由於AC自動機預處理,時間空間複雜度為\(O(n|\Sigma|)\)
然後考慮對於樹上每一個字首在AC自動機上匹配,每次從父親轉移過來,複雜度為\(O(n)\)
然後是常見的AC自動機操作,\(fail\)樹上的子樹累和即可
需要詢問離線,因此有一定侷限性
#include<bits/stdc++.h> using namespace std; #pragma GCC optimize(2) #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i) char IO; int rd(){ int s=0; while(!isdigit(IO=getchar())); do s=(s<<1)+(s<<3)+(IO^'0'); while(isdigit(IO=getchar())); return s; } const int N=1e6+10; int n,m,len,F[N],A[N]; char s[N],t[N]; int nxt[N][26],fail[N],cnt,pos[N]; int Q[N],L=1,R; void Build(){ rep(i,0,25) if(nxt[0][i]) Q[++R]=nxt[0][i]; while(L<=R) { int u=Q[L++]; rep(i,0,25) { int &v=nxt[u][i]; if(v) fail[v]=nxt[fail[u]][i],Q[++R]=v; else v=nxt[fail[u]][i]; } } } int main(){ n=rd(),m=rd(); rep(i,1,n) scanf("%c",s+i),F[i]=rd(); rep(i,1,m) { scanf("%s",t+1); int now=0; drep(j,strlen(t+1),1) { int c=t[j]-'A'; if(!nxt[now][c]) nxt[now][c]=++cnt; now=nxt[now][c]; } pos[i]=now; } Build(); rep(i,1,n) A[F[i]=nxt[F[F[i]]][s[i]-'A']]++; drep(i,R,1) A[fail[Q[i]]]+=A[Q[i]]; rep(i,1,m) printf("%d\n",A[pos[i]]); }