題意:屢次操做和詢問,操做是增長新的模版串,詢問是詢問匹配。node
思路:若是每次插入重建AC自動機的話,須要重建n次,每次重建複雜度爲n^2。ios
弄兩個AC自動機A和B,B限制結點數上限爲sqrt(n),每次增長新的模版串的時候將它插到B中,重建B,當B結點數達到sqrt(n)時,將B合併到A中,重建A。每次查詢在A和B,取和便可。複雜度:重建B爲sqrt(n),對B重建n次,故耗在B中的複雜度爲n*sqrt(n),重建A爲n,重建n/sqrt(n)=sqrt(n)次,在A中的複雜度也是n*sqrt(n),合併的複雜度爲sqrt(n),合併sqrt(n)次,故耗在合併的複雜度爲n(不包括重建A),所以總複雜度爲o(n*sqrt(n)+n*sqrt(n)+n)=o(n*sqrt(n))。這樣就將原本n^2的複雜度均攤成了n*sqrt(n)。。。數組
因爲重建的時候若是按平時那樣將真實邊與失配邊一視同仁的話,重建的時候處理起來比較麻煩(我還沒想好怎麼處理),因此這裏我將失配邊和真實邊區分開,這裏須要注意創建AC自動機的時候若是求f[ch[u][c]]須要沿着失配邊往回走而不能直接設爲ch[f[u]][c],由於這裏已經將真實邊與失配邊區分開了,因此ch[f[u]][c]可能不存在。另一個要注意的是重複出現的模版串,個人處理是弄一個vis數組判重,查完後再查一遍將vis清零,不直接memset固然是考慮到複雜度的問題。經過構造數據我已經發現了這兩個可能出錯的地方,調了一下交上去,原本覺得應該AC的,然而仍是WA了。。。爲什麼這樣做弄我!!!ide
先留着代碼,吃完飯回來繼續調。。。函數
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=5000100; const int INF=1e9+10; int q; char s[maxn],t[maxn]; bool vis[maxn]; struct Trie { int ch[maxn][2]; int End[maxn]; int last[maxn]; int f[maxn]; int rt,tot; int newnode() { ++tot; memset(ch[tot],-1,sizeof(ch[tot])); End[tot]=0; return tot; } void init() { tot=-1; rt=newnode(); } void insert(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; if(ch[u][c]==-1) ch[u][c]=newnode(); u=ch[u][c]; } End[u]++; } int get(int u) { if(u==rt||vis[u]) return 0; vis[u]=1; return End[u]+get(last[u]); } void cls(int u) { if(u==rt||vis[u]==0) return; vis[u]=0; cls(last[u]); } void recover(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; while(ch[u][c]==-1&&u!=rt) u=f[u]; if(ch[u][c]==-1) continue; u=ch[u][c]; if(End[u]) cls(u); else if(last[u]) cls(last[u]); } } int find(char *s) { int len=strlen(s),u=rt; int res=0; REP(i,0,len-1){ int c=s[i]-'0'; while(ch[u][c]==-1&&u!=rt) u=f[u]; if(ch[u][c]==-1) continue; u=ch[u][c]; if(End[u]) res+=get(u); else if(last[u]) res+=get(last[u]); } recover(s);/// search again to clear the vis return res; } void build() { queue<int> q; f[rt]=rt;last[rt]=rt; REP(c,0,1){ if(~ch[rt][c]) f[ch[rt][c]]=rt,q.push(ch[rt][c]); ///else ch[rt][c]=rt; } while(!q.empty()){ int u=q.front();q.pop(); REP(c,0,1){ if(~ch[u][c]){ int t=f[u]; while(ch[t][c]==-1&&t!=rt) t=f[t]; if(ch[t][c]==-1) f[ch[u][c]]=rt; else f[ch[u][c]]=ch[t][c]; q.push(ch[u][c]); } ///else ch[u][c]=ch[f[u]][c]; } if(End[f[u]]) last[u]=f[u]; else last[u]=last[f[u]]; } } };Trie A,B; void Insert(int u,int v) { A.End[u]+=B.End[v]; REP(c,0,1){ if(~B.ch[v][c]){ if(A.ch[u][c]==-1) A.ch[u][c]=A.newnode(); Insert(A.ch[u][c],B.ch[v][c]); } } } void join() { Insert(A.rt,B.rt); B.init();B.build(); A.build(); } void Move(char *s,int k) { int len=strlen(s); REP(i,0,len) t[i]=s[i]; REP(i,k,len-1) s[i-k]=t[i]; REP(i,1,k) s[len-i]=t[k-i]; s[len]='\0'; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif // ONLINE_JUDGE int T;cin>>T;int casen=1; while(T--){ scanf("%d",&q); A.init(); B.init(); A.build(); B.build(); printf("Case #%d:\n",casen++); MS0(vis); int ans=0; while(q--){ scanf("%s",s); if(s[0]=='+'){ B.insert(s+1); B.build(); if(B.tot+1>2010) join();/// join B to A } else{ Move(s+1,ans); //cout<<"s="<<s<<endl; ans=A.find(s+1)+B.find(s+1); //cout<<" A="<<A.find(s+1)<<" B="<<B.find(s+1)<<endl; printf("%d\n",ans); } } } return 0; } /** 4 3 +01 +01 ?01001 3 +01 ?010 ?011 7 +10010 +10010 ?00 ?0 ?10001010010010 +1111 ?1100101111 5 +111 +1010 ?10101111 +1111 ?00101111 2 5 +111 +1010 ?10101111 +1111 ?00101111 4 +111 +1010 +1111 ?10111100 */
悲劇地發現了題目看錯。。。原來是找有詢問的串中包含了多少模板串,模板串要去掉重複的。。。這樣就不須要vis數組清零了。。。直接沿着失配邊往回走就能夠了,每次往回走其實是遍歷該結點所表示的前綴的後綴,前綴的後綴就是全部的子串了。。。ui
然而仍是WA了。。。spa
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=5000100; const int INF=1e9+10; int q; char s[maxn],t[maxn]; struct Trie { int ch[maxn][2]; int f[maxn]; int last[maxn]; int End[maxn]; int rt,tot; int newnode() { ++tot; memset(ch[tot],-1,sizeof(ch[tot])); End[tot]=0; return tot; } void init() { tot=-1; rt=newnode(); } void insert(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; if(ch[u][c]==-1) ch[u][c]=newnode(); u=ch[u][c]; } End[u]=1; } void build() { queue<int> q; f[rt]=rt;last[rt]=rt; REP(c,0,1){ if(~ch[rt][c]) f[ch[rt][c]]=rt,q.push(ch[rt][c]); } while(!q.empty()){ int u=q.front();q.pop(); REP(c,0,1){ if(~ch[u][c]){ int t=f[u]; while(ch[t][c]==-1&&t!=rt) t=f[t]; if(ch[t][c]==-1) f[ch[u][c]]=rt; else f[ch[u][c]]=ch[t][c]; q.push(ch[u][c]); } } if(End[f[u]]) last[u]=f[u]; else last[u]=last[f[u]]; } } int get(int u) { if(u==rt) return 0; return End[u]+get(last[u]); } int find(char *s) { int len=strlen(s),u=rt; int res=0; REP(i,0,len-1){ int c=s[i]-'0'; while(ch[u][c]==-1&&u!=rt) u=f[u]; if(ch[u][c]==-1) continue; u=ch[u][c]; if(End[u]) res+=get(u); else if(last[u]) res+=get(last[u]); } return res; } bool has(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; if(ch[u][c]==-1) return 0; u=ch[u][c]; } return End[u]; } };Trie A,B; void Insert(int u,int v) { A.End[u]|=B.End[v]; REP(c,0,1){ if(~B.ch[v][c]){ if(A.ch[u][c]==-1) A.ch[u][c]=A.newnode(); Insert(A.ch[u][c],B.ch[v][c]); } } } void join() { Insert(A.rt,B.rt);/// insert B to A B.init();B.build(); A.build(); } void MoveL(char *s,int k) { int len=strlen(s); REP(i,0,len) t[i]=s[i]; s[0]='\0'; REP(i,0,len-k-1) s[i]=t[k+i]; REP(i,0,k-1) s[len-k+i]=t[i]; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif // ONLINE_JUDGE int T;cin>>T;int casen=1; while(T--){ scanf("%d",&q); A.init();B.init(); A.build();B.build(); printf("Case #%d:\n",casen++); int ans=0; while(q--){ scanf("%s",s); MoveL(s+1,ans); if(s[0]=='+'){ if(A.has(s+1)||B.has(s+1)) continue; B.insert(s+1); B.build(); if(B.tot>2010) join();/// join B to A } else{ ans=A.find(s+1)+B.find(s+1); printf("%d\n",ans); } } } return 0; } /** 4 3 +01 +01 ?01001 3 +01 ?010 ?011 7 +10010 +10010 ?00 ?0 ?10001010010010 +1111 ?1100101111 5 +111 +1010 ?10101111 +1111 ?00101111 2 5 +111 +1010 ?10101111 +1111 ?00101111 4 +111 +1010 +1111 ?10111100 */
終於過了。。。在AC自動機上調半天,最後發現只是move函數寫錯了。。。左移k位按原來的寫法須要注意k>=len的時候,能夠直接讓k對len取餘。這題真的沒什麼坑點,不過第一次想出這個思路的人很厲害,後來人理解和實現這個思路確實沒什麼難度。我真是太傻逼了,在細節上掛半天。。。code
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=5000100; const int INF=1e9+10; int q; char s[maxn],t[maxn]; struct Trie { int ch[maxn][2]; int f[maxn]; int last[maxn]; int End[maxn]; int rt,tot; int newnode() { ++tot; memset(ch[tot],-1,sizeof(ch[tot])); End[tot]=0; return tot; } void init() { tot=-1; rt=newnode(); } void insert(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; if(ch[u][c]==-1) ch[u][c]=newnode(); u=ch[u][c]; } End[u]=1; } void build() { queue<int> q; f[rt]=rt;last[rt]=rt; REP(c,0,1){ if(~ch[rt][c]) f[ch[rt][c]]=rt,q.push(ch[rt][c]); } while(!q.empty()){ int u=q.front();q.pop(); REP(c,0,1){ if(~ch[u][c]){ int t=f[u]; while(ch[t][c]==-1&&t!=rt) t=f[t]; if(ch[t][c]==-1) f[ch[u][c]]=rt; else f[ch[u][c]]=ch[t][c]; q.push(ch[u][c]); } } if(End[f[u]]) last[u]=f[u]; else last[u]=last[f[u]]; } } ll get(int u) { if(u==rt) return 0; return End[u]+get(last[u]); } ll find(char *s) { int len=strlen(s),u=rt; ll res=0; REP(i,0,len-1){ int c=s[i]-'0'; while(ch[u][c]==-1&&u!=rt) u=f[u]; if(ch[u][c]==-1) continue; u=ch[u][c]; if(End[u]) res+=get(u); else if(last[u]) res+=get(last[u]); } return res; } bool has(char *s) { int len=strlen(s),u=rt; REP(i,0,len-1){ int c=s[i]-'0'; if(ch[u][c]==-1) return 0; u=ch[u][c]; } return End[u]; } };Trie A,B; void Insert(int u,int v) { A.End[u]|=B.End[v]; REP(c,0,1){ if(~B.ch[v][c]){ if(A.ch[u][c]==-1) A.ch[u][c]=A.newnode(); Insert(A.ch[u][c],B.ch[v][c]); } } } void join() { Insert(A.rt,B.rt);/// insert B to A B.init(); A.build(); } void MoveL(char *s,int k) { int len=strlen(s); REP(i,0,len) t[i]=s[i]; REP(i,0,len-1) s[i]=t[(i+k)%len]; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif // ONLINE_JUDGE int T;cin>>T;int casen=1; while(T--){ scanf("%d",&q); A.init();B.init(); printf("Case #%d:\n",casen++); ll ans=0; while(q--){ scanf("%s",s); MoveL(s+1,ans); if(s[0]=='+'){ if(A.has(s+1)||B.has(s+1)) continue; B.insert(s+1); B.build(); if(B.tot>2010) join();/// join B to A } else{ ans=A.find(s+1)+B.find(s+1); printf("%I64d\n",ans); } } } return 0; } /** 4 3 +01 +01 ?01001 3 +01 ?010 ?011 7 +10010 +10010 ?00 ?0 ?10001010010010 +1111 ?1100101111 5 +111 +1010 ?10101111 +1111 ?00101111 2 5 +111 +1010 ?10101111 +1111 ?00101111 4 +111 +1010 +1111 ?10111100 */