【HDU4787】GRE Words Revenge-AC自动机+分块

测试地址:GRE Words Revenge
题目大意:维护以下操作:1.在词典中加入一个单词。2.询问一个字符串S中有多少个子串是词典中的单词。强制在线
做法:本题需要用到AC自动机+分块。
如果离线,我们可以直接把所有涉及的单词先加入到trie中,然后建AC自动机,在运行的时候打标记和匹配即可。
但这里强制在线,我们知道AC自动机本身是不能支持在线插入操作的,所以每次插入后我们需要暴力重构AC自动机,重构一次的时间复杂度是O(n)的,最坏时间复杂度就是O(n2),难以接受。
注意到,插入的时间复杂度是均摊O(1)的,重构的复杂度是均摊O(n)的,看到这样悬殊的复杂度,我们就想到用分块思想来降低瓶颈,也就是重构操作的复杂度。
具体来说,我们维护一大一小两个AC自动机,给小的AC自动机设定一个容量为n。每次插入一个单词,先往小的自动机中插入,每次插入后暴力重构小自动机。如果一次插入后,小自动机中的点数超过容量,就暴力把小自动机中的信息加入到大自动机中,然后重构大自动机。
现在我们来分析时间复杂度:对于插入操作,每个点最多被加入两次(小自动机,大自动机),因此时间复杂度是O(n)。对于重构操作,有n次对小自动机的重构,时间复杂度为O(nn),还有n次对大自动机的重构,时间复杂度也为O(nn)
这样我们就能在线维护出AC自动机了。处理询问的方法和传统的AC自动机类似,在两个自动机上都匹配一遍即可(当然,需要在构建AC自动机的时候求出一个表示每个点贡献值的数组cnt),但是需要注意一点:同一个单词不应该在两边同时拥有贡献,不然会算重。这样我们就完成了这一题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,rt0,rt1,buf,len,tot;
int ch[200010][2]={0},fail[200010];
int q[200010],h,t;
ll cnt[200010],ans;
char s[10000010];
bool word[200010]={0};

void insertbuf(int &v1,int v2,int step)
{
    if (!v1)
    {
        v1=++tot;
        buf++;
        ch[v1][0]=ch[v1][1]=word[v1]=0;
    }
    if (step>=len)
    {
        if (!word[v2]) word[v1]=1;
        else word[v1]=0;
        return;
    }
    bool f=s[step]-'0';
    insertbuf(ch[v1][f],ch[v2][f],step+1);
}

void insertmajor(int &v1,int v2)
{
    if (!v1)
    {
        v1=++tot;
        ch[v1][0]=ch[v1][1]=word[v1]=0;
    }
    word[v1]|=word[v2];
    if (ch[v2][0]) insertmajor(ch[v1][0],ch[v2][0]);
    if (ch[v2][1]) insertmajor(ch[v1][1],ch[v2][1]);
}

void rebuild(int rt)
{
    h=1,t=0;
    cnt[rt]=0;
    for(int i=0;i<=1;i++)
        if (ch[rt][i])
        {
            fail[ch[rt][i]]=rt;
            cnt[ch[rt][i]]=cnt[rt]+word[ch[rt][i]];
            q[++t]=ch[rt][i];
        }
    while(h<=t)
    {
        int v=q[h++];
        for(int i=0;i<=1;i++)
            if (ch[v][i])
            {
                int p=fail[v];
                while(p!=rt&&!ch[p][i]) p=fail[p];
                if (ch[p][i]) fail[ch[v][i]]=ch[p][i];
                else fail[ch[v][i]]=rt;
                cnt[ch[v][i]]=cnt[fail[ch[v][i]]]+word[ch[v][i]];
                q[++t]=ch[v][i];
            }
    }
}

void match(int rt)
{
    int now=rt,i=1;
    while(i<len)
    {
        bool f=s[i++]-'0';
        while(now!=rt&&!ch[now][f]) now=fail[now];
        if (ch[now][f]) now=ch[now][f];
        else now=rt;
        ans+=cnt[now];
    }
}

void decrypt()
{
    int k=ans%(len-1);
    for(int i=1;i<=k;i++)
        s[len-1+i]=s[i];
    for(int i=1;i<=len;i++)
        s[i]=s[i+k];
}

int main()
{
    scanf("%d",&T);
    int Case=0;
    while(T--)
    {
        Case++;
        printf("Case #%d:\n",Case);
        rt0=1,rt1=2;
        buf=0;ans=0;
        ch[rt0][0]=ch[rt0][1]=ch[rt1][0]=ch[rt1][1]=0;
        tot=2;

        scanf("%d",&n);
        int blocksiz=400;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            if (s[0]=='+')
            {
                len=strlen(s);
                decrypt();
                insertbuf(rt1,rt0,1);
                if (buf>blocksiz)
                {
                    insertmajor(rt0,rt1);
                    rebuild(rt0);
                    ch[rt1][0]=ch[rt1][1]=0;
                    buf=0;
                }
                else rebuild(rt1);
            }
            else
            {
                len=strlen(s);
                decrypt();
                ans=0;
                match(rt0),match(rt1);
                printf("%lld\n",ans);
            }
        }
    }

    return 0;
}
posted @ 2018-08-08 11:50  Maxwei_wzj  阅读(113)  评论(0编辑  收藏  举报