bzoj2555 SubString (后缀自动机+LCT)

bzoj2555 SubString

题意:两个操作:在原来的字符串后面加上一个新的字符串;查询一个字符串出现了几次

方法:假如没有假如新字符串的要求,就是模板了。
那么考虑加上新字符串会有什么影响,假如新的字符串以后,有的fa就会改变,而其他的是不会变的。
因此考虑LCT维护。每次连边, link(p,fa[p]) l i n k ( p , f a [ p ] ) ,那么fa[p]到根的size就会加上 size[p] s i z e [ p ]
删边(更改fa), cut(p) c u t ( p ) ,也就是把p和它的fa断开,那么fa[p]到根也就要减去 size[p] s i z e [ p ]
也就是路径加、单点查询(就不需要update了…….)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 600010
char str[N],op[10];
int last=0,root,cnt=0,Q,mask=0,mx[N<<1],fa[N<<1],son[N<<1][26],size[N<<1],tag[N<<1],Fa[N<<1],ch[N<<1][2];
inline void add(int x,int y){
    tag[x]+=y;size[x]+=y; 
}
inline bool isroot(int x){return x!=ch[Fa[x]][0] && x!=ch[Fa[x]][1];}
inline void rotate(int x){
    int y=Fa[x],z=Fa[y],t=ch[y][0]==x;
    if(!isroot(y)) ch[z][ch[z][1]==y]=x;
    Fa[y]=x;Fa[x]=z;Fa[ch[x][t]]=y;
    ch[y][t^1]=ch[x][t];ch[x][t]=y;
}
inline void pushdown(int x){
    if(!tag[x]) return;int l=ch[x][0],r=ch[x][1];
    size[l]+=tag[x];size[r]+=tag[x];
    tag[l]+=tag[x];tag[r]+=tag[x];
    tag[x]=0;
}
void push(int x){
    if(!isroot(x)) push(Fa[x]);
    pushdown(x);
}
inline void splay(int x){
    push(x);
    while(!isroot(x)){
        int y=Fa[x];
        if(isroot(y)){rotate(x);return;}
        if(ch[y][0]==x^ch[Fa[y]][0]==y) rotate(x);
        else rotate(y);rotate(x);
    }
}
inline void access(int x){
    int y=0;
    while(x){
        splay(x);ch[x][1]=y;
        y=x;x=Fa[x];
    }
}
inline void link(int x,int y){
    Fa[x]=y;
    access(y),splay(y);add(y,size[x]);
}
inline void cut(int x){
    access(x);splay(x);add(ch[x][0],-size[x]);
    Fa[ch[x][0]]=0;ch[x][0]=0;
}
inline void ins(int ch){
    int p=last,np=++cnt;last=np;mx[np]=mx[p]+1;size[np]=1;
    while(p && !son[p][ch]) son[p][ch]=np,p=fa[p];
    if(!p) fa[np]=root,link(np,root);
    else{
        int q=son[p][ch];
        if(mx[q]==mx[p]+1) fa[np]=q,link(np,q);
        else{
            int nq=++cnt;mx[nq]=mx[p]+1;
            memcpy(son[nq],son[q],sizeof(son[nq]));
            fa[nq]=fa[q];fa[q]=fa[np]=nq;
            cut(q);link(nq,fa[nq]);link(q,nq);link(np,nq);
            while(son[p][ch]==q) son[p][ch]=nq,p=fa[p];
        }
    } 
}
int main(){
    scanf("%d%s",&Q,str+1);
    int n=strlen(str+1);last=root=++cnt;
    for(int i=1;i<=n;i++) ins(str[i]-'A');
    while(Q--){
        scanf("%s%s",op,str+1);n=strlen(str+1);int mask1=mask;
        for(int i=1;i<=n;i++) mask1=(mask1*131+i-1)%n,swap(str[i],str[mask1+1]);
        if(op[0]=='A') for(int i=1;i<=n;i++) ins(str[i]-'A');
        else{
            int p=root,f=1;
            for(int i=1;i<=n;i++){
                if(!son[p][str[i]-'A']){puts("0");f=0;break;}
                else p=son[p][str[i]-'A'];
            }if(f){splay(p);printf("%d\n",size[p]);mask^=size[p];}
        }
    }
    return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值