You should process m queries over a set D of strings. Each query is one of three kinds:
Add a string s to the set D. It is guaranteed that the string s was not added before.
Delete a string s from the set D. It is guaranteed that the string s is in the set D.
For the given string s find the number of occurrences of the strings from the set D. If some string p from D has several occurrences in s you should count all of them.
Note that you should solve the problem in online mode. It means that you can’t read the whole input at once. You can read each query only after writing the answer for the last query of the third type. Use functions fflush in C++ and BufferedWriter.flush in Java languages after each writing in your program.
Input
The first line contains integer m (1 ≤ m ≤ 3·105) — the number of queries.
Each of the next m lines contains integer t (1 ≤ t ≤ 3) and nonempty string s — the kind of the query and the string to process. All strings consist of only lowercase English letters.
The sum of lengths of all strings in the input will not exceed 3·105.
Output
For each query of the third kind print the only integer c — the desired number of occurrences in the string s.
Example
Input
5
1 abc
3 abcabc
2 abc
1 aba
3 abababc
Output
2
2
Input
10
1 abc
1 bcd
1 abcd
3 abcd
2 abcd
3 abcd
2 bcd
3 abcd
2 abc
3 abcd
Output
3
2
1
0
题目大意:给M个操作 1.插入一个串S 2.删除一个串S 3.给出一个串S,询问之前存在(插入后未被删除的)的串在S中出现的次数。
因为ac自动机 不支持删除和修改操作 每次都要重新配置 失配指针会爆炸。
大佬们都是用 ac自动机各种合并做的 太强了。。弱弱表示只会板子,别的都不会
因为字符串总长度不超过3e5 那么就很可以做文章了
我们把低于1000的存进trie树里面 那么最多也就300个串 肯定不到,因为还有查询串的长度。
然后根据一个串的子串就是所有后缀的前缀,所以 遍历一下查询串每次查询一次 其实也就查询了
查询串长度*1000 肯定到不了。当时可以把小串的都查询遍了,然后对于大于1000的串,暴力kmp就好,因为trie树是支持删除操作的。
大于1000的串也是顶多不到100个,复杂度和上面差不多,另外加个标记是删除还是增加 统计进去就好,总的复杂也就1e7左右
#include <bits/stdc++.h>
using namespace std;
const int MAXN=3e5+100;
const int CASE_SIZE=26;
typedef long long ll;
char str[MAXN];
struct Trie
{
int child[MAXN][CASE_SIZE],value[MAXN],trieN,root;
void init()
{
trieN=root=0;value[root]=0;
memset(child[root],-1,sizeof(child[root]));
}
int newnode()
{
trieN++;value[trieN]=0;
memset(child[trieN],-1,sizeof(child[trieN]));
return trieN;
}
void insert(char *s,int val)
{
int x=root;
for(int i=0;s[i];i++)
{
int d=s[i]-'a';
if(child[x][d]==-1)
child[x][d]=newnode();
x=child[x][d];
}
value[x]+=val;//这种是读完整串才能++
}
int search(char *s)
{
int sum=0,x=root;
for(int i=0;s[i];i++)
{
int d=s[i]-'a';
if(child[x][d]==-1)
break;
x=child[x][d];
sum+=value[x];
}
return sum;
}
}trie;
struct KMP{
int Next[MAXN];
void getNext(string s,int len){
memset(Next,0,sizeof(Next));
int i=0,j=-1;Next[0]=-1;
while(i<len)
{
if(j==-1||s[i]==s[j]){
++i;++j;
Next[i]=j;
}
else j=Next[j];
}
}
int search(string s,char *str)
{
int sum=0,j=0,lens=s.length(),lenstr=strlen(str);
getNext(s,s.length());
for(int i=0;i<lenstr;i++)
{
while(j>0&&s[j]!=str[i])
{
j=Next[j];
}
if(s[j]==str[i])
j++;
if(j==lens)
{
sum++;j=Next[j];
}
}
return sum;
}
}kmp;
string cun[MAXN];
int flag[MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int cnt=0;
memset(flag,0,sizeof(flag));
trie.init();
for(int i=1;i<=n;i++)
{
int d;
scanf("%d %s",&d,str);
int len=strlen(str);
if(d==1)
{
if(len<=1000)
trie.insert(str,1);
else cun[++cnt]=string(str),flag[cnt]=1;
}
if(d==2)
{
if(len<=1000)
trie.insert(str,-1);
else cun[++cnt]=string(str),flag[cnt]=-1;
}
if(d==3)
{
ll ans=0;
for(int i=0;i<len;i++)
ans+=trie.search(str+i);
for(int i=1;i<=cnt;i++)
{
if(len<cun[i].length()) continue;
ans+=kmp.search(cun[i],str)*flag[i];
}
printf("%I64d\n",ans );
fflush(stdout);
}
}
}
}