• CF710F String Set Queries (AC自动机+二进制分组)


    题目链接

    http://codeforces.com/problemset/problem/710/F

    题意

    维护一个字符串集合,支持三种操作:
    1.加字符串
    2.删字符串
    3.查询集合中的所有字符串在给出的模板串中出现的次数
    操作数&字符串总长(≤3×10^5)

    思路

    看到多串匹配考虑用AC自动机。
    先考虑删除,这个很好办,我们可以维护两个AC自动机,一个记录插入,一个记录删除,将串在两个上面分别跑再做差就好了。这样子删除也变成了插入。
    题目难点主要是插入,对于每个新来的串都重构,(TLE)是肯定的。
    于是乎就有了二项堆的思想:维护二项堆核心思想就是二进制分组(第(i)个堆拥有(2^i)个元素,第(i)个堆可以由两个第(i−1)个堆合并而成)
    类似的,这里可以将堆换为AC自动机:(i)号AC自动机维护(2^i)个串,若有两个(i)号AC自动机则合并成一个(i+1)号AC自动机,合并是拆除原AC自动机,然后暴力合并。这样复杂度就变成(log)的了

    #include<bits/stdc++.h>
    using namespace std;
    const int maxx = 3e5+10;
    struct ac
    {
        int trie[maxx][26],tmp[maxx][26];
        int fail[maxx],vis[maxx],rt[maxx],siz[maxx],sum[maxx];
        int tot,top;
        void getfail(int x)
        {
            queue<int>q;
            for(int i=0;i<26;i++)
            {
                tmp[x][i]=trie[x][i];
                if(tmp[x][i])fail[tmp[x][i]]=x,q.push(tmp[x][i]);
                else tmp[x][i]=x;
            }
            while(!q.empty())
            {
                int u=q.front();q.pop();
                for(int i=0;i<26;i++)
                {
                    tmp[u][i]=trie[u][i];
                    if(tmp[u][i])fail[tmp[u][i]]=tmp[fail[u]][i],q.push(tmp[u][i]);
                    else tmp[u][i]=tmp[fail[u]][i];
                }
                sum[u]=vis[u]+sum[fail[u]];
            }
        }
        int Merge(int x,int y)
        {
            if(!x||!y)return x+y;
            vis[x]+=vis[y];
            for(int i=0;i<26;i++)trie[x][i]=Merge(trie[x][i],trie[y][i]);
            return x;
        }
        void Insert(string s)
        {
            rt[++top]=++tot;
            siz[top]=1;
            int now=rt[top];
            for(int i=0;i<s.size();i++)
            {
                int id=s[i]-'a';
                if(!trie[now][id])trie[now][id]=++tot;
                now=trie[now][id];
            }
            vis[now]=1;
            while(top&&siz[top]==siz[top-1]) //二进制分组合并
            {
                top--;
                rt[top]=Merge(rt[top],rt[top+1]);
                siz[top]+=siz[top+1];
            }
            getfail(rt[top]);
        }
        int query(string s)
        {
            int ans=0;
            for(int i=1;i<=top;i++) //将当前所有分组的结果累加就行了
            {
                int now=rt[i];
                for(int j=0;j<s.size();j++)
                {
                    int id=s[j]-'a';
                    now=tmp[now][id];
                    ans+=sum[now];
                }
            }
            return ans;
        }
    }ac1,ac2;
    int main()
    {
        int n;
        cin>>n;
        int op;
        string s;
        while(n--)
        {
            cin>>op>>s;
            if(op==1)ac1.Insert(s);
            else if(op==2)ac2.Insert(s);
            else printf("%d
    ",ac1.query(s)-ac2.query(s));
        }
        return 0;
    }
    
  • 相关阅读:
    搭建CDH的yum本地源
    搭建一个离线yum源
    VMware主机 几次断电后,挂载的磁盘报错了,系统无法启动
    vm workstation15 迁移至ESXi6.7步奏
    ESXi6.7在 ASUS PRIME B360-PLUS 主板上安装报错
    php常用函数
    PHP函数
    php语句
    TSQL语句练习题
    php的基础
  • 原文地址:https://www.cnblogs.com/HooYing/p/12482392.html
Copyright © 2020-2023  润新知