题意:维护一个字符串集合,支持插入一个串,删除一个串,询问集合中的串在给出的串中出现多少次,一个串出现多次算多次。并且不会有两次插入的串相同。强制在线。
考虑没有删除的情况,将插入串的个数二进制拆分,对于每
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;