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\n",ac1.query(s)-ac2.query(s));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/HooYing/p/12482392.html