HDU 4787 GRE Words Revenge 在線AC自動機

題意:屢次操做和詢問,操做是增長新的模版串,詢問是詢問匹配。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

*/
View Code

 悲劇地發現了題目看錯。。。原來是找有詢問的串中包含了多少模板串,模板串要去掉重複的。。。這樣就不須要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

*/
View Code

 終於過了。。。在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

*/
View Code
相關文章
相關標籤/搜索