BZOJ 2555: SubString
2555: SubString
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 2556 Solved: 764
[Submit][Status][Discuss]
Description
懒得写背景了,给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
Input
第一行一个数Q表示操作个数
第二行一个字符串表示初始字符串init
接下来Q行,每行2个字符串Type,Str
Type是ADD的话表示在后面插入字符串。
Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
为了体现在线操作,你需要维护一个变量mask,初始值为0
读入串Str之后,使用这个过程将之解码成真正询问的串TrueStr。
询问的时候,对TrueStr询问后输出一行答案Result
然后mask = mask xor Result
插入的时候,将TrueStr插到当前字符串后面即可。
HINT:ADD和QUERY操作的字符串都需要解压
Output
Sample Input
A
QUERY B
ADD BBABBBBAAB
Sample Output
HINT
40 % 的数据字符串最终长度 <= 20000,询问次数<= 1000,询问总长度<= 10000
100 % 的数据字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000
新加数据一组--2015.05.20
Source
分析:
据说是$SAM$的裸题...
考虑每一次加入一个串就直接增量法建初后缀自动机,每一次询问就在$SAM$上去匹配,找到匹配完成的节点输出$|endpos|$,那么其实这道题的点大概就在于加入一个字符之后如何动态维护每个点的$|endpos|$,考虑加入一个字符,只会让这个字符在$fail$上到根结点的那条路径上的点的$|endpos|$加$1$,所以我们可以用$LCT$来维护每个点的权值,然后每一次要把路径权值$+1$就打个标记...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=1300000+5; int n,mask,last=1,tail=1,Max[maxn],Min[maxn],nxt[maxn][26],fail[maxn]; int son[maxn][2],val[maxn],add[maxn],father[maxn]; char s[maxn],opt[13]; inline bool isroot(int x){ if(father[x]==0) return true; int f=father[x]; if(son[f][0]==x) return false; if(son[f][1]==x) return false; return true; } inline void pushdown(int x){ if(add[x]){ if(son[x][0]) val[son[x][0]]+=add[x],add[son[x][0]]+=add[x]; if(son[x][1]) val[son[x][1]]+=add[x],add[son[x][1]]+=add[x]; add[x]=0; } } inline void push(int t){ static int stk[maxn]; int top=0; stk[top++]=t; while(!isroot(t)) stk[top++]=t=father[t]; while(top) pushdown(stk[--top]); } inline void connect(int f,int t,int k){ if(t) father[t]=f; if(f) son[f][k]=t; } inline void rotate(int t){ int f=father[t]; int g=father[f]; int s=(son[f][1]==t); connect(f,son[t][!s],s); connect(t,f,!s); father[t]=g; if(g&&son[g][0]==f) son[g][0]=t; if(g&&son[g][1]==f) son[g][1]=t; } inline void splay(int t){ // cout<<"splay: "<<t<<endl; push(t); while(!isroot(t)){ int f=father[t]; int g=father[f]; if(isroot(f)) rotate(t); else{ bool a=(f&&son[f][1]==t); bool b=(g&&son[g][1]==f); if(a==b) rotate(f),rotate(t); else rotate(t),rotate(t); } } } inline void access(int t){ // cout<<"access: "<<t<<endl; int p=0,x=t; while(t){ splay(t); son[t][1]=p; p=t,t=father[t]; } splay(x); } inline void link(int t,int f){ // cout<<"link: "<<t<<" "<<f<<endl; splay(t);father[t]=f; } inline void cut(int t){ // cout<<"cut: "<<t<<endl; access(t);int x=son[t][0]; father[x]=son[t][0]=0; } inline void build(char *s){ while(*s){ int p=last,t=++tail,c=*s++-'A'; Max[t]=Max[p]+1; // cout<<"char: "<<(char)(c+'A')<<endl; while(p&&!nxt[p][c]) nxt[p][c]=t,p=fail[p]; if(p){ int q=nxt[p][c]; if(Max[q]==Max[p]+1) fail[t]=q,Min[t]=Max[q]+1; else{ int k=++tail; fail[k]=fail[q]; fail[q]=fail[t]=k; Max[k]=Max[p]+1; Min[q]=Max[k]+1; Min[t]=Max[k]+1; Min[k]=Max[fail[k]]+1; memcpy(nxt[k],nxt[q],26*sizeof(int)); while(p&&nxt[p][c]==q) nxt[p][c]=k,p=fail[p]; // puts("hahahahahah"); cut(q);link(k,fail[k]),link(q,k);val[k]=val[q]; } } else Min[t]=fail[t]=1; last=t;link(t,fail[t]); // puts("finish link"); access(t); // puts("finish access"); add[t]++,val[t]++; } } inline void decodewithmask(int tmp){ int len=strlen(s); for(int i=0;i<len;i++){ tmp=(tmp*131+i)%len; swap(s[i],s[tmp]); } } inline int solve(char *s){ int u=1; while(*s){ int c=*s++-'A'; if(nxt[u][c]) u=nxt[u][c]; else return 0; } access(u); return val[u]; } signed main(void){ // freopen("SubString1.in","r",stdin); scanf("%d%s",&n,s); build(s); for(int i=1,ans;i<=n;i++){ scanf("%s%s",opt,s);decodewithmask(mask); // puts(s); if(opt[0]=='Q') printf("%d\n",ans=solve(s)),mask^=ans; else build(s); } return 0; }
$By NeighThon$