zoukankan      html  css  js  c++  java
  • HDU4787_GRE Words Revenge

    这个题目做得泪牛满面。

    题目为给你若干串,有的表示添加一个串,有的表示询问一个串有多少个字串为前面出现过的串。

    题目一看就知道肯定是AC自动机(不过后缀自动机也是可以的)

    但是细想就会发现AC自动机好像不支持在线修改。如果你每次读入一个串就重构一次AC自动机的话,那么时间复杂度达到了N^2,肯定会T的。

    于是就产生了一种奇葩的解法。

    搞两个自动机,一个自动机为大的自动机,一个自动机为小的自动机(用于缓冲)。每次我都只把字符串加入到小的自动机里面并且重构小自动机,当小自动机的容量超过了sqrt(L)的时候,我们把小自动机合并到大自动机上,这样算下来时间复杂度为O(L*sqrt(L)),每次询问的答案就是大小自动机上查询答案的和,好像是可以AC的。

    但是问题来了,怎么合并两个自动机呢?

    其实很简单,自动机相对于字典树来说有什么区别呢?一个是tire树,一个是tire图,自动机多了fail指针还多了一些新建立的next指针。所以我们只要把这些多余的指针去掉,然后对小字典树做一次搜索就可以吧两个字典树合并了。然后重构了一次大自动机,小自动机清空,就可以了。

    总的时间复杂度有点勉强,不过算下来10^5好像理论上来说也是可以过的。

    赞一个,题目出的太好了。

    不过听说还有用后缀自动机做,并且用动态树来维护的。不明觉厉啊。求神犇指点。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define maxn 100050
    using namespace std;
    
    int next[2][maxn][2],has[2][maxn][2],tag[2][maxn],sum[2][maxn],fail[2][maxn],N[2];
    char s0[50*maxn],s[50*maxn];
    int qx[maxn],qy[maxn],tq;
    int ans,n,t;
    
    int add(int x)
    {
        N[x]++;
        next[x][N[x]][0]=next[x][N[x]][1]=0;
        has[x][N[x]][0]=has[x][N[x]][1]=0;
        tag[x][N[x]]=0;
        sum[x][N[x]]=0;
        fail[x][N[x]]=0;
        return N[x];
    }
    
    void init(int x)
    {
        N[x]=-1;
        N[x]=add(x);
    }
    
    void initall()
    {
        ans=0;
        init(0),init(1);
    }
    
    void getstring()
    {
        scanf("%s",s0);
        int L=strlen(s0)-1,cur=0;
        for (int i=ans%L+1; s0[i]; i++) s[cur]=s0[i],cur++;
        for (int i=1; i<=ans%L; i++) s[cur]=s0[i],cur++;
        s[L]=0;
    }
    
    void destory(int x)
    {
        for (int i=0; i<=N[x]; i++)
        {
            fail[x][i]=0;
            for (int j=0; j<2; j++)
                if (has[x][i][j]==0) next[x][i][j]=0;
        }
    }
    
    void insert()
    {
        int cur=0,tep;
        for (int i=0; s[i]; i++)
        {
            tep=s[i]-'0';
            if (next[1][cur][tep]==0)
            {
                next[1][cur][tep]=add(1);
                has[1][cur][tep]=1;
            }
            cur=next[1][cur][tep];
        }
        tag[1][cur]=1;
    }
    
    void union01()
    {
        int cur=1;
        qx[1]=qy[1]=0;
        while (cur>0)
        {
            int xx=qx[cur],yy=qy[cur];
            if (tag[1][yy]) tag[0][xx]=1;
            cur--;
            for (int i=0; i<2; i++)
            {
                if (next[1][yy][i])
                {
                    if (next[0][xx][i]==0)
                    {
                        next[0][xx][i]=add(0);
                        has[0][xx][i]=1;
                    }
                    cur++;
                    qx[cur]=next[0][xx][i];
                    qy[cur]=next[1][yy][i];
                }
            }
        }
    }
    
    void AC_build(int x)
    {
        for (int i=0; i<=N[x]; i++) sum[x][i]=tag[x][i];
        queue<int> Q;
        int cur,child;
        Q.push(0);
        while (!Q.empty())
        {
            cur=Q.front(),Q.pop();
            for (int i=0; i<2; i++)
            {
                child=next[x][cur][i];
                if (child)
                {
                    Q.push(child);
                    if (cur==0) fail[x][child]=0;
                    else
                    {
                        fail[x][child]=next[x][fail[x][cur]][i];
                        sum[x][child]+=sum[x][fail[x][child]];
                    }
                }
                else next[x][cur][i]=next[x][fail[x][cur]][i];
            }
        }
    }
    
    int query(int x)
    {
        int cur=0,tep,tot=0;
        for (int i=0; s[i]; i++)
        {
            tep=s[i]-'0';
            cur=next[x][cur][tep];
            tot+=sum[x][cur];
        }
        return tot;
    }
    
    bool find(int x)
    {
        int cur=0,tep;
        for (int i=0; s[i]; i++)
        {
            tep=s[i]-'0';
            if (has[x][cur][tep]) cur=next[x][cur][tep];
                else return false;
        }
        if (tag[x][cur]==0) return false;
        return true;
    }
    
    int main()
    {
        int cas=0;
        scanf("%d",&t);
        while (t--)
        {
            printf("Case #%d:
    ",++cas);
            initall();
            scanf("%d",&n);
            while (n--)
            {
                getstring();
                if (s0[0]=='+')
                {
                    if (find(1) || find(0)) continue;
                    destory(1);
                    insert();//insert s to trie 1
                    if (N[1]>=1000)
                    {
                        destory(0);
                        union01();
                        AC_build(0);
                        init(1);
                    }
                    else AC_build(1);
                }
                else if (s0[0]=='?')
                {
                    ans=query(0)+query(1);
                    printf("%d
    ",ans);
                }
            }
        }
        return 0;
    }
    如有转载,请注明出处(http://www.cnblogs.com/lochan)
  • 相关阅读:
    利用CSS计数函数counter()实现计数
    弹跳加载动画特效Bouncing loader
    HTML页面中解决内容元素随窗口变化布局变乱问题
    CSS中列表项list样式
    框模型中设置内容区域元素占地尺寸box-sizing属性
    PHP100视频教程-->视频下载
    HTML页面中5种超酷的伪类选择器:hover效果
    HTML中获取input中单选按钮radio数据(性别例子)
    HTML中 DOM操作的Document 对象详解(收藏)
    14、输入一个链表,从尾到头打印链表每个节点的值。
  • 原文地址:https://www.cnblogs.com/lochan/p/3430824.html
Copyright © 2011-2022 走看看