[bzoj2555][后缀平衡树][后缀自动机]SubString

2555: SubString

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 2857 Solved: 856
[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

2



A



QUERY B



ADD BBABBBBAAB

Sample Output

0

HINT

40 % 的数据字符串最终长度 <= 20000,询问次数<= 1000,询问总长度<= 10000

100 % 的数据字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000

新加数据一组–2015.05.20

Source

Ctsc模拟赛By 洁妹

ps:这篇题解是纪念我学的后缀平衡树,主要提了提后缀平衡树的用法。
sol:
题目的强制在线有坑点,mask只有在mask = mask xor Result时改变结果,其他时候都是局部变量。

这道题我当做后缀平衡树的裸题来做了。
下面阐述一下后缀平衡树

节点编号x存的是字符在字符串中的位置,这个x在树中的位置表示其后缀的大小,后缀平衡树只支持在前面插字符,这相当于是插入了一个后缀。如果题目要求在后面插,那么我们可以视作这个字符串的后缀为其前缀,做起来没差别。
我的后缀平衡树使用treap来达到重量平衡。使得保证树的形态的同时,使其为key(rand)值的小根堆。
后缀平衡树支持几个经典操作
1:插入字符(即某个后缀)
2:查询某个后缀(可以不包含在本串中)在树中的排名
3:查询排名为x的某个后缀
后缀平衡树的实质为动态的sa,因此sa支持的操作后缀平衡树都支持。

1:
朴素算法:插入字符时,要先和当前节点的后缀比较大小,一位一位的比较,直到某位不同时比出大小。这个显然太慢
优化算法:我们发现只需要比较第一位,之后的后缀在之前处理过。因此我们可以通过询问2个后缀的排名来比较,实现log^2的插入。
优越算法:我们对后缀平衡树中的每个节点进行规定,设其区间为(l,r),则这个节点的值为mid,左儿子的区间为(l,mid),右儿子为(mid+1,r),值直接代表该后缀在后缀平衡树中的相对大小。则我们在插入字符时可以O(1)的比较后缀。

我们应该怎么实现这个标号法呢?由于重量平衡的性质,每个点的子树大小期望为O(1),因此我们在每次旋转操作的时候,可以直接暴力修改整个子树的标号值。因为重量平衡的性质,所以树高为log级别,标号每次都/2,所以我们开long long就能够把区间存下来了。

2:
查询某个后缀的排名,就是带着一个串在后缀平衡树上走。
朴素算法:和当前节点一位一位比较
优化算法(假的):二分,用字符串哈希求LCP比较。
需要注意的是,朴素算法一般比优化算法快。

3:
从这个节点往上走记录size即可。

正文:

本题要求在字符串后面插入字符,那就把前缀看成后缀即可。
求字符串x在文本串中出现了几次,实际上就是求
小于x#的后缀的数目 - 小于x的后缀的数目(#为最大字符)
本题前缀是后缀,就是把#放前面

#include<cstdio> 
#include<algorithm> 
#include<string> 
#include<cstring> 
#include<cstdlib> 
#include<cmath> 
#include<iostream> 
#define ll long long
using namespace std; 
const int N=600005; 
int n,m,rt,tot; 
ll rank[N]; 
struct tree{int son[2],key;}a[N];
int key[N];
char sr[N]; 
inline int read() 
{ 
    char c; 
    bool flag=false; 
    while((c=getchar())>'9'||c<'0') 
    if(c=='-')flag=true; 
    int res=c-'0'; 
    while((c=getchar())>='0'&&c<='9') 
    res=(res<<3)+(res<<1)+c-'0'; 
    return flag?-res:res; 
} 
int size[N]; 
inline void updata(int x,ll l,ll r) 
{ 
    if(!x) return; 
    size[x]=1; 
    rank[x]=l+r>>1; 
    updata(a[x].son[0],l,rank[x]); 
    updata(a[x].son[1],rank[x]+1,r); 
    size[x]=size[a[x].son[0]]+size[a[x].son[1]]+1; 
} 
inline bool cmp(int x,int y) 
{ 
    return sr[x]<sr[y]||sr[x]==sr[y]&&rank[x-1]<rank[y-1]; 
} 
inline void rotate(int x,int y,ll l,ll r) 
{ 
    int Side=a[y].son[1]==x; 
    a[y].son[Side]=a[x].son[Side^1]; 
    a[x].son[Side^1]=y; 
    updata(x,l,r); 
} 
inline void insert(int &x,ll l,ll r) 
{ 
    ll mid=l+r>>1; 
    if(!x) 
    { 
        x=++tot; 
        size[x]=1;
        a[x].key=rand();
        rank[x]=mid; 
        return; 
    } 
    size[x]++; 
    if(cmp(tot+1,x)) insert(a[x].son[0],l,mid); 
    else insert(a[x].son[1],mid+1,r); 
    if(a[tot].key<a[x].key) 
    { 
        rotate(tot,x,l,r); 
        x=tot; 
    } 
} 
int l,len,mask,lastAns=0; 
char s[N],Sr[N]; 
inline void get_ready(int mask) 
{ 
    scanf("%s",s); 
    l=strlen(s); 
    for(int i=0;i<l;++i) 
    { 
        mask=(mask*131+i)%l; 
        swap(s[i],s[mask]); 
    } 
} 
int P=2333;
inline void lcp(int x,int &LCP)
{
    int e=min(l+1,x);
    for(int i=1;i<=e;++i)
    if(sr[x-i+1]==s[l-i+1]) LCP++;
    else break;
}
inline int cmp_(int x)//1表示x比较小 0表示大 2= 
{
    if(sr[x]!=s[l]) return sr[x]<s[l];
    int LCP=0;
    lcp(x,LCP);
    if(LCP==l+1&&LCP==x) return 2;
    if(LCP==l+1) return 0;
    if(LCP==x) return 1;
    return sr[x-LCP]<s[l-LCP];
}
inline int get_rank(int x) 
{ 
    if(!x) return 0;
    int flag=cmp_(x);
    if(flag==1) return size[a[x].son[0]]+1+get_rank(a[x].son[1]);
    else if(flag==0) return get_rank(a[x].son[0]);
    else if(flag==2) return size[a[x].son[0]];
} 
int main() 
{ 
//  freopen("SubString.in","r",stdin); 
//  freopen("SubString.out","w",stdout); 
    srand(19260817);
    m=read(); 
    scanf("%s",sr+1); 
    len=strlen(sr+1); 
    for(int i=1;i<=len;++i) insert(rt,1,(ll)1<<62);
    while(m--)
    {
        scanf("%s",Sr+1); 
        get_ready(mask); 
        if(Sr[1]=='A')
        for(int j=0;j<l;++j) 
        { 
            sr[++len]=s[j];
            insert(rt,1,(ll)1<<62); 
        } 
        if(Sr[1]=='Q') 
        {
            if(l>len)
            {
                lastAns=0; 
                printf("%d\n",lastAns);
                mask^=lastAns;
                continue;
            }
            --l;
            lastAns=-get_rank(rt);
            for(int j=l;j>=0;--j)
            s[j+1]=s[j];
            s[0]=(char)127;
            ++l;
            lastAns+=get_rank(rt); 
            printf("%d\n",lastAns);
            mask^=lastAns; 
        } 
    } 
}

后缀自动机

有生之年还是学了后缀自动机
关于后缀自动机的部分可以看一下我的小结。
考虑这题怎么做,因为要维护right集合大小,所以在np代替p的时候,要更新一下np一直到root的parent,这个东西用lct维护一下即可。
我不是很清楚我一开始维护的是子树信息,但是那种做法错了,后来改成了链的做法就过了。

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;

int n,m;
const int N=1210000;

int last=1,root=1,tot=1;
void ins(int x,int y,int v);
inline int read()
{
    char c;
    bool pd=0;
    while((c=getchar())>'9'||c<'0')
    if(c=='-') pd=1;
    int res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=(res<<3)+(res<<1)+c-'0';
    return pd?-res:res;
}
int lc[N],rc[N],sum1[N],sum2[N],fa[N],real[N],add[N];
bool rev[N];
inline bool is_root(int x)
{
    return lc[fa[x]]!=x&&rc[fa[x]]!=x;
}
inline void tag_rev(int x)
{
    rev[x]=!rev[x];
    swap(lc[x],rc[x]);
}
inline void tag_down(int x)
{
    if(rev[x])
    {
        tag_rev(lc[x]);
        tag_rev(rc[x]);
        rev[x]=0;
    }
    if(add[x])
    {
        add[lc[x]]+=add[x];
        add[rc[x]]+=add[x];
        sum1[lc[x]]+=add[x];
        sum1[rc[x]]+=add[x];
        add[x]=0;
    }
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=lc[y]==x?rc[x]:lc[x];
    if(b) fa[b]=y;
    fa[x]=z;fa[y]=x;
    if(z)
    {
        if(lc[z]==y) lc[z]=x;
        if(rc[z]==y) rc[z]=x;
    }
    if(lc[y]==x) lc[y]=b,rc[x]=y;
    else rc[y]=b,lc[x]=y;
}
int sta[N];
inline void splay(int x)
{
    sta[sta[0]=1]=x;
    for(int y=x;!is_root(y);y=fa[y]) sta[++sta[0]]=fa[y];
    while(sta[0]) tag_down(sta[sta[0]--]);
    while(!is_root(x))
    {
        if(!is_root(fa[x]))
        {
            if((lc[fa[x]]==x)==(lc[fa[fa[x]]]==fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
}
inline void access(int q)
{
    for(int p=0;q;p=q,q=fa[q])
    {
        splay(q);
        sum2[q]+=sum1[rc[q]];
        rc[q]=p;
        sum2[q]-=sum1[rc[q]];
    }
}
inline void make_root(int x)
{
    access(x);
    splay(x);
    tag_rev(x);
}
inline void link(int x,int y)
{
    make_root(x);
    fa[x]=y;
}
inline void cut(int x,int y)
{
    make_root(x);
    access(y);
    splay(x);
    rc[x]=fa[y]=0;
}
inline void ins(int x,int y,int v)
{
    make_root(x);
    access(y);
    splay(x);
    add[x]+=v;
    sum1[x]+=v;
}
struct cc
{
    int val,go[26],par;
}state[N];
inline void extend(int c)
{
    ++m;
    int p=last;
    int np=++tot;
    last=np;
    state[np].val=m;
    real[np]=1;
    sum1[np]=1;
    while(p&&!state[p].go[c]) {state[p].go[c]=np;p=state[p].par;}
    if(!p) {state[np].par=root;link(np,root);ins(root,root,1);return;}
    int q=state[p].go[c];
    if(state[q].val==state[p].val+1) {state[np].par=q;link(np,q);ins(q,root,1);return;}
    int nq=++tot;

    splay(q);
    sum1[nq]=sum1[q];

    state[nq].val=state[p].val+1;
    for(int i=0;i<=25;++i) state[nq].go[i]=state[q].go[i];
    state[nq].par=state[q].par;
    link(nq,state[q].par);
    cut(q,state[q].par);
    link(q,nq);
    state[q].par=nq;
    link(np,nq);
    state[np].par=nq;

    ins(nq,root,1);

    while(state[p].go[c]==q) state[p].go[c]=nq,p=state[p].par;
}
int mask;
char sr[N];
inline void get(int mask)
{
    scanf("%s",sr);
    n=strlen(sr);
    for(int j=0;j<n;++j)
    {
        mask=(mask*131+j)%n;
        swap(sr[j],sr[mask]);
    }
}
int main()
{
//  freopen("2555.in","r",stdin);
    int q=read();
    scanf("%s",sr+1);
    n=strlen(sr+1);
    for(int i=1;i<=n;++i) extend(sr[i]-'A');
    for(int i=1;i<=q;++i)
    {
        scanf("%s",sr+1);
        if(sr[1]=='A')
        {
            get(mask);
            for(int j=0;j<n;++j)
            extend(sr[j]-'A');
        }
        else
        {
            get(mask);
            int now=root,ans=0;
            for(int j=0;j<n;++j)
            {
                if(!state[now].go[sr[j]-'A'])
                {
                    ans=-1;
                    break;
                }
                now=state[now].go[sr[j]-'A'];
            }
            if(ans==-1) {printf("0\n");continue;}
//          make_root(root);
//          access(now);
//          splay(root);
            splay(now);
            printf("%d\n",sum1[now]);
            mask^=sum1[now];
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值