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

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

#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

 

posted @ 2016-03-10 12:47  __560  阅读(560)  评论(0编辑  收藏  举报