HDU 4787 GRE Words Revenge 在线AC自动机

Posted __560

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于HDU 4787 GRE Words Revenge 在线AC自动机的主要内容,如果未能解决你的问题,请参考以下文章

hdu 4787 GRE Words Revenge 在线AC自动机

HDU 4787 GRE Words Revenge 在线AC自动机

hdu 4117 -- GRE Words (AC自动机+线段树)

综合(奇技淫巧):HDU 5118 GRE Words Once More!

HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)

题解GRE Words(UVA1502)