P5212 SubString(SAM+LCT)

5 篇文章 0 订阅
5 篇文章 0 订阅

P5212 SubString

题目描述

给定一个字符串 init,要求支持两个操作:

  • 在当前字符串的后面插入一个字符串。

  • 询问字符串 s s s 在当前字符串中出现了几次。(作为连续子串)

强制在线。

输入格式

第一行一个整数 Q Q Q 表示操作个数。

第二行一个字符串表示初始字符串 init

接下来 Q Q Q 行,每行 2 2 2 个字符串 Type,Str

  • TypeADD,表示在后面插入字符串。

  • TypeQUERY,表示询问某字符串在当前字符串中出现了几次。

为了体现在线操作,你需要维护一个变量 mask,初始值为 0 0 0

String decodeWithMask(String s, int mask) {
	char[] chars = s.toCharArray();
	for (int j = 0; j < chars.length; j++) {
		mask = (mask * 131 + j) % chars.length;
		
		char t = chars[j];
		chars[j] = chars[mask];
		chars[mask] = t;
	}
	return new String(chars);
}

读入串 Str 之后,使用这个过程将之解码成真正询问的串 TrueStr

询问的时候,对 TrueStr 询问后输出一行答案 Result

然后 m a s k = m a s k ⨁ R e s u l t mask=mask \bigoplus Result mask=maskResult

插入的时候,将TrueStr插到当前字符串后面即可。

注意:ADDQUERY 操作的字符串都需要解压。

输出格式

对于每一个 QUERY 操作,输出询问的字符串在当前字符串中出现了几次。

样例 #1

样例输入 #1

2
A
QUERY B
ADD BBABBBBAAB

样例输出 #1

0

如果不要求强制在线的话,把询问离线下来按照右端点排序,把插入字符串的过程改成删除字符串的过程,对于删除一个字符,就可以把这个字符对应的后缀树上的结点到根的链的 s i z e size size全都减1,这个用树链剖分就可以很好的维护。
但是如果强制在线的话,就需要动态维护后缀树了。
这个时候单单使用树剖就行不通了,需要更为强大的数据结构 L C T LCT LCT来解决。
考虑如何用 L C T LCT LCT动态维护后缀树,这就需要分情况讨论:
S A M SAM SAM中插入结点的时候有两种情况,一种是在后缀树上插入一个点,一种是插入两个点,对于一个点的情况,我们只需要在 L C T LCT LCT上连接两个点即可,而对于两个点的情况,天上的结点需要继承儿子的权值,地上的结点直接连边就可,这样就可以动态维护后缀树了,对于每次插入的地上的结点,我们还需要把这个点到根上的路径 s i z e size size都+1,这个可以通过 L C T LCT LCT的链修改操作很好的维护,只需要 s p l i t split split这个链,然后在 s p l a y splay splay树的根上打标记即可。不要忘记 p u s h d o w n pushdown pushdown
对于每一个询问,可以先通过 S A M SAM SAM D A G DAG DAG走到这个字符串的位置,然后这个点在后缀树上的 s i z e size size就是我们要求的答案。

#include<bits/stdc++.h>
#define clean(x) memset(x,0,sizeof(x))
#define fil(x,n) fill(x,x+1+n,0)
#define inf 2000000009
#define maxn 3000005
// #define int long long
using namespace std;

int read()
{
    int x=1,res=0;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        x=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        res=res*10+(c-'0');
        c=getchar();
    }
    return res*x;
}
char a[maxn],b[maxn];

void Mask(char c[], int mask) {
	for (int j=0;j<strlen(c);j++) {
		mask=(mask*131+j)%strlen(c);
		char t=c[j];
		c[j]=c[mask];
		c[mask]=t;
	}
    for(int j=0;j<=strlen(c);j++) b[j]=c[j];
}

struct edge{
    int next,to;
};

struct LCT{
    int top,ch[maxn][2],f[maxn],xr[maxn],q[maxn],rev[maxn],val[maxn];
    long long sum[maxn];
    int tag_mul[maxn],tag_add[maxn],sz[maxn],p[maxn];
    #define ls(x) ch[x][0]
    #define rs(x) ch[x][1]
    void access(int x){for(int t=0;x;t=x,x=f[x])splay(x),ch[x][1]=t,pushup(x);}
    void makeroot(int x){access(x);splay(x);rev[x]^=1;}
    int find(int x){access(x);splay(x);while(ch[x][0])x=ch[x][0];return x;}
    void split(int x,int y){makeroot(x);access(y);splay(y);}
    bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
    void link(int x,int y){makeroot(x);if(find(y)!=x)f[x]=y;}
    void cut(int x,int y){
        if(find(x)!=find(y)) return; split(x,y);
        if(ch[y][0]==x&&ch[x][1]==0) ch[y][0]=0,f[x]=0;
        if(ch[y][1]==x&&ch[x][0]==0) ch[y][1]=0,f[x]=0;
    }
    void pushup(int x){
        // xr[x]=xr[ch[x][0]]^xr[ch[x][1]]^val[x];
        sz[x]=(sz[ls(x)]+sz[rs(x)]+p[x]);
        sum[x]=(val[x]+sum[ls(x)]+sum[rs(x)]);
    }
    void pushdown(int x){
        int l=ch[x][0],r=ch[x][1];
        if(rev[x]){
            rev[l]^=1;rev[r]^=1;rev[x]^=1;
            swap(ch[x][0],ch[x][1]);
        }
        if(tag_add[x]){
            tag_add[ls(x)]+=tag_add[x];
            val[ls(x)]+=tag_add[x];
            sum[ls(x)]+=sz[ls(x)]*tag_add[x];
            tag_add[rs(x)]+=tag_add[x];
            val[rs(x)]+=tag_add[x];
            sum[rs(x)]+=sz[rs(x)]*tag_add[x];
            tag_add[x]=0;
        }
    }
    void rotate(int x){
        int y=f[x],z=f[y],l,r;
        if(ch[y][0]==x)l=0;else l=1;r=l^1;
        if(!isroot(y)){if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
        f[x]=z;f[y]=x;f[ch[x][r]]=y;
        ch[y][l]=ch[x][r];ch[x][r]=y;
        pushup(y);pushup(x);
    }
    void splay(int x){
        top=1;q[top]=x;
        for(int i=x;!isroot(i);i=f[i]) q[++top]=f[i];
        for(int i=top;i;i--) pushdown(q[i]);
        while(!isroot(x)){
            int y=f[x],z=f[y];
            if(!isroot(y)){
                if((ch[y][0]==x)^(ch[z][0]==y)) rotate(x);
                else rotate(y);
            } rotate(x);
        }
    }
}lct;

struct SAM{
    int id[maxn],pos[maxn],tot=1,lt=1,num,l[maxn],ch[maxn][26],sz[maxn],f[maxn],last[maxn];
    edge g[maxn];
    void init(int len){
        for(int i=0;i<=len;i++){
            id[i]=0;pos[i]=0;f[i]=0;pos[i]=0;
            last[i]=0;l[i]=0;sz[i]=0;
            for(int j=0;j<26;j++) ch[i][j]=0;
        }
        tot=lt=1;num=0;
    }
    void insert(int c,int i){
        int v=++tot,u=lt;lt=tot;pos[tot]=i;id[i]=tot;
        sz[v]=1;l[v]=l[u]+1;lct.p[v]=1;
        while(u&&!ch[u][c]) {ch[u][c]=v;u=f[u];}
        if(!u) {f[v]=1;lct.link(v,1);return;}
        int x=ch[u][c];
        if(l[x]==l[u]+1) {f[v]=x;lct.link(v,x);return;}
        int y=++tot;pos[y]=pos[x];
        lct.split(x,x);
        lct.val[y]=lct.val[x];lct.sum[y]=lct.sum[x];lct.sz[y]=lct.sz[x];
        lct.cut(x,f[x]);lct.link(y,f[x]);
        lct.link(x,y);lct.link(v,y);
        l[y]=l[u]+1;f[y]=f[x];f[x]=f[v]=y;
        memcpy(ch[y],ch[x],sizeof(ch[x]));
        while(u&&ch[u][c]==x) {ch[u][c]=y;u=f[u];}
    }
    void add(int from,int to)
    {
        g[++num].next=last[from];
        g[num].to=to;
        last[from]=num;
    }
    void dfs(int x)
    {
        for(int i=last[x];i;i=g[i].next)
        {
            int v=g[i].to;
            dfs(v);
            sz[x]+=sz[v];
        }
    }
}sam;

signed main()
{
    int q=read();
    scanf("%s",a+1);
    int mask=0,ans,len=strlen(a+1);
    for(int i=1;i<=len;i++){
        sam.insert(a[i]-'A',i);
        lct.split(sam.lt,1);
        lct.tag_add[1]+=1;
        lct.val[1]+=1;
        lct.sum[1]+=lct.sz[1];
    } 
    while(q--)
    {
        string op;cin>>op;
        if(op=="ADD"){
            cin>>a;
            Mask(a,mask);int l=strlen(a);
            for(int i=0;i<l;i++){
                sam.insert(b[i]-'A',i);
                lct.split(sam.lt,1);
                lct.tag_add[1]+=1;
                lct.val[1]+=1;
                lct.sum[1]+=lct.sz[1];
            }
        }
        if(op=="QUERY"){
            cin>>a;
            Mask(a,mask); int u=1,l=strlen(a);
            for(int i=0;i<l;i++){
                int c=b[i]-'A';
                u=sam.ch[u][c];
            }
            if(!u) ans=0;else{lct.split(u,u);ans=lct.sum[u];}
            cout<<ans<<endl;
            mask=mask^ans;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snowy2002

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值