zoukankan      html  css  js  c++  java
  • HDU 4787 GRE Words Revenge 在线AC自动机

    题意:多次操作和询问,操作是增加新的模版串,询问是询问匹配。

    思路:如果每次插入重建AC自动机的话,需要重建n次,每次重建复杂度为n^2。

      弄两个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了。。。为何这样作弄我!!!

    先留着代码,吃完饭回来继续调。。。

    #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]='';
    }
    
    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:
    ",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
    ",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数组清零了。。。直接沿着失配边往回走就可以了,每次往回走实际上是遍历该结点所表示的前缀的后缀,前缀的后缀就是所有的子串了。。。

    然而还是WA了。。。

    #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]='';
        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:
    ",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
    ",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取余。这题真的没什么坑点,不过第一次想出这个思路的人很厉害,后来人理解和实现这个思路确实没什么难度。我真是太傻逼了,在细节上挂半天。。。

    #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:
    ",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
    ",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不了的题,只有不努力的ACMER!
  • 相关阅读:
    java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener问题解决
    Unix
    Win7 扩容磁盘分区
    在Java中怎样高效的推断数组中是否包括某个元素
    2.JAVA编程思想——一切都是对象
    Cookie-Parser是怎样解析签名后的cookie的(同一时候对cookie和cookie-signature进行说明)
    hive 索引
    Javascript属性constructor/prototype的底层原理
    HTML5 的四个亮点
    零基础学python-1.5 第一个程序
  • 原文地址:https://www.cnblogs.com/--560/p/5261460.html
Copyright © 2011-2022 走看看