【CF710F】String Set Queries(二进制分组,AC自动机)

【CF710F】String Set Queries(二进制分组,AC自动机)

题面

洛谷
CF
翻译:
你有一个字符集合\(D\),初始为空,
有三种操作:
\(D\)中加入一个串;从\(D\)中删除一个串;给定一个串\(S\),询问\(D\)中的串在\(S\)中总共出现了多少次。

题解

询问显然就是将\(S\)放在所有\(D\)构成的\(AC\)自动机上跑。
所以我们需要一种方法,可以动态的支持\(AC\)自动机的插入以及删除。
先考虑删除,这个很好办,我们可以维护两个\(AC\)自动机,一个记录插入,一个记录删除,将串在两个上面分别跑再做差就好了。这样子删除也变成了插入。
那么如何插入?
我们对于串二进制分组,因为串和串之间是独立的,所以可以对于每一组的串建立一个\(AC\)自动机,合并块的时候直接重构\(AC\)自动机就好了。
写起来很爽啊。我又回到了C++STL选手???string真好用

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAX 300300
#define pb push_back
struct Node{int son[26],fail,v;}t[MAX];
int St[MAX],top;
int NewNode(){return St[top--];}
void Insert(int u,string s)
{
    for(int i=0,l=s.length();i<l;++i)
    {
        if(!t[u].son[s[i]-97])t[u].son[s[i]-97]=NewNode();
        u=t[u].son[s[i]-97];
    }
    t[u].v+=1;
}
void Del(int rt)
{
    if(!rt)return;
    for(int i=0;i<26;++i)
        Del(t[rt].son[i]),t[rt].son[i]=0;
    t[rt].fail=t[rt].v=0;St[++top]=rt;
}
struct Group
{
    vector<string> p;int tot,rt;
    void insert(string s){p.pb(s);++tot;}
    void clear(){Del(rt);rt=NewNode();p.clear();tot=0;}
    void Build()
        {
			Del(rt);rt=NewNode();
            for(int i=0;i<tot;++i)Insert(rt,p[i]);
            queue<int> Q;t[rt].fail=rt;
            for(int i=0;i<26;++i)
                if(t[rt].son[i])Q.push(t[rt].son[i]),t[t[rt].son[i]].fail=rt;
            while(!Q.empty())
            {
                int u=Q.front();Q.pop();
                for(int i=0;i<26;++i)
                    if(t[u].son[i])
                    {
                        int p=t[u].fail,v=t[u].son[i];
                        while(p!=rt&&!t[p].son[i])p=t[p].fail;
                        if(t[p].son[i])t[v].fail=t[p].son[i];
                        else t[v].fail=rt;
                        Q.push(v);
                        t[v].v+=t[t[v].fail].v;
                    }
            }
        }
    int Query(string s)
        {
            int ret=0,u=rt;
            for(int i=0,l=s.length();i<l;++i)
            {
                int c=s[i]-97;
                if(t[u].son[c])u=t[u].son[c];
                else
                {
                    int p=t[u].fail;
                    while(p!=rt&&!t[p].son[c])p=t[p].fail;
                    if(t[p].son[c])u=t[p].son[c];
                    else u=rt;
                }
                ret+=t[u].v;
            }
            return ret;
        }
}A[20],B[20];
int tp1,tp2;
void Insert(string s)
{
    ++tp1;A[tp1].clear();A[tp1].insert(s);
    while(tp1>1&&A[tp1].tot==A[tp1-1].tot)
	{
        for(int i=0;i<A[tp1].tot;++i)A[tp1-1].insert(A[tp1].p[i]);
		A[tp1--].clear();
	}
	A[tp1].Build();
}
void Delete(string s)
{
    ++tp2;B[tp2].clear();B[tp2].insert(s);
    while(tp2>1&&B[tp2].tot==B[tp2-1].tot)
	{
        for(int i=0;i<B[tp2].tot;++i)B[tp2-1].insert(B[tp2].p[i]);
		B[tp2--].clear();
	}
	B[tp2].Build();
}
int Query(string s)
{
    int ret=0;
    for(int i=1;i<=tp1;++i)ret+=A[i].Query(s);
    for(int i=1;i<=tp2;++i)ret-=B[i].Query(s);
    return ret;
}
int m,opt;
string s;
int main()
{
    ios::sync_with_stdio(false);
    for(int i=1;i<MAX;++i)St[++top]=i;
    cin>>m;
    while(m--)
    {
        cin>>opt>>s;
        if(opt==1)Insert(s);
        if(opt==2)Delete(s);
        if(opt==3)cout<<Query(s)<<endl;
    }
    return 0;
}

posted @ 2018-08-13 16:10  小蒟蒻yyb  阅读(685)  评论(0编辑  收藏  举报