cf710F. String Set Queries
题解: 我们考虑到 对于一个AC自动机而言 建好fair指针后 不能再插入字符串并维护fair指针 所以我们考虑暴力的做法 在加入字符串的同时 并重新构造fair指针 这样的复杂度是接受不了的 因为对于一个字符串而言他会被操作n次 我们考虑到一个优化是说 对于第一个插入的字符串我们没必要一直在后面的操作中重构它 我们想到用2进制加法这样优化构造过程 类似的 对于每一次进位 考虑进位的这个位置是否有trie树 若没有则在这个位置构造Fair 否则继续进位 到没有为止 这样保证了每个字符串只会被操作logn次 复杂度得到保证
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=3e5+10; const double eps=1e-8; #define ll long long using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } typedef struct AC{ int fair[MAXN],ch[MAXN][26],st[MAXN],tot=0,sum[MAXN],cnt=0,rt[21],key[MAXN]; queue<int>que; AC(){ inc(i,0,20)rt[i]=0; } void newnode(int &x){ if(tot)x=st[tot--]; else x=++cnt; fair[x]=sum[x]=key[x]=0; inc(i,0,25)ch[x][i]=0; } void insert(char s[]){ newnode(rt[0]); int len=strlen(s);int temp=rt[0]; for(int i=0;i<len;i++){ int t=s[i]-'a'; if(!ch[temp][t])newnode(ch[temp][t]); temp=ch[temp][t]; } key[temp]=1; } void merge(int &x,int y){ if(x){sum[x]=fair[x]=0;} if(!y)return ; if(!x){x=y;return ;} st[++tot]=y;key[x]+=key[y]; for(int i=0;i<26;i++)merge(ch[x][i],ch[y][i]); } void Fair(int x){ que.push(x); while(!que.empty()){ int t=que.front();que.pop(); for(int i=0;i<26;i++){ if(!ch[t][i])continue; if(!fair[t]){fair[ch[t][i]]=x;sum[ch[t][i]]=key[ch[t][i]];que.push(ch[t][i]);continue;} int temp=fair[t]; for(;temp&&!ch[temp][i];temp=fair[temp]); if(!temp)fair[ch[t][i]]=x,sum[ch[t][i]]=key[ch[t][i]]; else fair[ch[t][i]]=ch[temp][i],sum[ch[t][i]]=sum[ch[temp][i]]+key[ch[t][i]]; que.push(ch[t][i]); } } } void Insert(char s[]){ insert(s); for(int i=1;i<=20;i++){ if(rt[i]){ merge(rt[i],rt[i-1]);rt[i-1]=0; } else{ merge(rt[i],rt[i-1]);rt[i-1]=0; Fair(rt[i]); break; } } } int querty(char s[]){ int len=strlen(s);int ans=0; for(int i=1;i<=20;i++){ if(!rt[i])continue; int temp=rt[i]; for(int j=0;j<len;j++){ int t=s[j]-'a'; if(!ch[temp][t]){ int p=temp; for(;p&&!ch[p][t];p=fair[p]); if(!p)temp=rt[i];else temp=ch[p][t]; } else temp=ch[temp][t]; ans+=sum[temp]; } } return ans; } }AC; AC *add,*delet; char str[MAXN]; int main(){ int m=read(); add=new AC();delet=new AC(); while(m--){ int op=read();scanf("%s",str); if(op==1)add->Insert(str); else if(op==2)delet->Insert(str); else printf("%d\n",add->querty(str)-delet->querty(str)); fflush(stdout); } return 0; }
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 Javalanguages after each writing in your program.
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.
For each query of the third kind print the only integer c — the desired number of occurrences in the string s.
5
1 abc
3 abcabc
2 abc
1 aba
3 abababc
2
2
10
1 abc
1 bcd
1 abcd
3 abcd
2 abcd
3 abcd
2 bcd
3 abcd
2 abc
3 abcd
3
2
1
0