zoukankan      html  css  js  c++  java
  • 【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:
    ",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
    ",ans);
                }
            }
        }
    
        return 0;
    }
  • 相关阅读:
    栈和其他寄存器大小
    checksec的安装及初步使用(新版)
    KMP算法之Next数组详解
    向上取整的三种方法
    C++STL(Standard Template Library,即标准模版库)——Map容器
    JS基础语法一
    JS函数学习
    JS对象学习(二)
    JavaStript对象学习(一)
    CSS3新特性学习(2)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793284.html
Copyright © 2011-2022 走看看