codeforces 710f String Set Queries ac自动机 二进制分组

题意:维护一个字符串集合,支持插入一个串,删除一个串,询问集合中的串在给出的串中出现多少次,一个串出现多次算多次。并且不会有两次插入的串相同。强制在线。

考虑没有删除的情况,将插入串的个数二进制拆分,对于每 2k 个串维护一个ac自动机。
这样共有 log 个ac自动机
。插入时如果当前ac自动机中串的个数和上一个个数相同,那么合并两个自动机。每个字符串只会被合并 log 次。

有删除时同样维护一个删除串的这样的东西,查询时减一下就行了。

#include <bits/stdc++.h>
using namespace std;
#define N 310000
int m,cnt;
char s[N];
struct acauto
{
    int ch[N][27],fail[N],en[N],en1[N],cnt;
    int num[31],root[31],top;
    queue<int>que;
    void acmatch(int x)
    {
        que.push(x);
        while(!que.empty())
        {
            int tmp=que.front();que.pop();
            for(int i=1;i<=26;i++)
                if(ch[tmp][i])
                {
                    int t=fail[tmp],t1=ch[tmp][i];
                    while(t&&!ch[t][i])t=fail[t];
                    fail[t1]=t ? ch[t][i]:x;
                    que.push(t1);
                    en1[t1]=en1[fail[t1]]+en[t1];
                }
        }
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x+y;
        en[x]=en[x]|en[y];
        for(int i=1;i<=26;i++)
            ch[x][i]=merge(ch[x][i],ch[y][i]);
        return x;
    }
    void insert()
    {
        scanf("%s",s+1);int len=strlen(s+1);
        root[++top]=++cnt;
        int now=root[top];
        for(int i=1,t;i<=len;i++)
        {
            if(!ch[now][t=s[i]-'a'+1])
                ch[now][t]=++cnt;
            now=ch[now][t];
        }
        en[now]=1;num[top]=1;
        acmatch(root[top]);
        while(top&&num[top]==num[top-1])
        {
            merge(root[top-1],root[top]);
            acmatch(root[top-1]);
            num[top-1]+=num[top];top--;
        }
    }
    int query(int len)
    {
        int ret=0;
        for(int i=1;i<=top;i++)
        {
            for(int now=root[i],j=1,t;j<=len;j++)
            {
                t=s[j]-'a'+1;
                while(now&&!ch[now][t])
                    now=fail[now];
                now= now ? ch[now][t]:root[i];
                ret+=en1[now];
            }
        }
        return ret;
    }
}ac1,ac2;
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d",&m);
    for(int t;m--;)
    {
        scanf("%d",&t);
        if(t==1)ac1.insert();
        else if(t==2)ac2.insert();
        else 
        {
            scanf("%s",s+1);int len=strlen(s+1);
            printf("%d\n",ac1.query(len)-ac2.query(len));
            fflush(stdout);
        }
    }
    return 0;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值