【splay】BZOJ 1014: [JSOI2008]火星人prefix

8 篇文章 0 订阅
1 篇文章 0 订阅

BZOJ 1014: [JSOI2008]火星人prefix


Description

火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度。比方说,LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0 在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。


Input

第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示: 1、 询问。语法:Q x y,x, y均为正整数。功能:计算LCQ(x, y) 限制:1 <= x, y <= 当前字符串长度。 2、 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。 3、 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x = 0,则在字符串开头插入。限制:x不超过当前字符串长度。


Output

对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。


Sample Input

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11


Sample Output

5
1
0
2
1


HINT

数据规模:
对于100%的数据,满足:
1、 所有字符串自始至终都只有小写字母构成。
2、 M <= 150,000
3、 字符串长度L自始至终都满足L <= 100,000
4、 询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。


solution

插入和修改都比较水吧。。

主要是最长公共前缀

用一个t[k].hsh维护k为根的子树的字符串的hash值

这样每次询问的时候我们就可以二分长度+判定hash值是否相同来搞了。(询问一段子串的hash值先把L-1旋到根,R+1旋到根的右子树,之后直接返回R+1点的左节点的hsh值)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

#define maxn 150001

int n,root,size;

unsigned long long v[150001];

struct splay{
    int l,r,size,fa;
    unsigned long long h,v;
}t[maxn*2];

char s[maxn];

void update(int k)
{
    t[k].size=t[t[k].l].size+t[t[k].r].size+1;
    t[k].h=t[t[k].l].h+t[k].v*v[t[t[k].l].size]+t[t[k].r].h*v[t[t[k].l].size+1];
}

void build(int l,int r,int f)
{
    if(r<l)return;
    if(l==r)
    {
        t[l].v=t[l].h=s[l]-'a'+1;
        t[l].size=1,t[l].fa=f;
        if(f>r)t[f].l=l;
        else t[f].r=l;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid-1,mid);build(mid+1,r,mid);
    t[mid].v=s[mid]-'a'+1;
    t[mid].fa=f;update(mid);
    if(mid<f)t[f].l=mid;
    else t[f].r=mid;
}

void raxe(int x,int &k)
{
    int y,z;
    y=t[x].fa;z=t[y].fa;
    if(y==k)k=x;
    else {if(t[z].l==y)t[z].l=x; else t[z].r=x;}
    t[x].fa=z,t[y].fa=x,t[t[x].r].fa=y;
    t[y].l=t[x].r,t[x].r=y;
    update(y),update(x);
}

void laxe(int x,int &k)
{
    int y,z;
    y=t[x].fa;z=t[y].fa;
    if(y==k)k=x;
    else {if(t[z].l==y)t[z].l=x; else t[z].r=x;}
    t[x].fa=z,t[y].fa=x,t[t[x].l].fa=y;
    t[y].r=t[x].l,t[x].l=y;
    update(y),update(x);
}

void splay(int x,int &k)
{
    int y,z;
    while(x!=k)
    {
        y=t[x].fa;z=t[y].fa;
        if(y!=k)
        {
            if(t[z].l==y&&t[y].l==x)raxe(y,k);
            else if(t[z].r==y&&t[y].r==x)laxe(y,k);
            else if(t[z].r==y&&t[y].l==x)raxe(x,k);
            else if(t[z].l==y&&t[y].r==x)laxe(x,k);
        }
        if(t[t[x].fa].l==x)raxe(x,k);
        else laxe(x,k);
    }
}

int find(int k,int x)
{
    if(t[t[k].l].size>=x)return find(t[k].l,x);
    else if(t[t[k].l].size+1<x)return find(t[k].r,x-t[t[k].l].size-1);
    else return k;
}

unsigned long long query(int x,int val)
{
    int ans1=find(root,x),ans2=find(root,x+val+2);
    splay(ans1,root);
    splay(ans2,t[root].r);
    return t[t[t[root].r].l].h;
}

int ask(int str1,int str2)
{
    if(str1>str2)swap(str1,str2);
    int l=0,r=size-str2-2,ans=-1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(query(str1,mid)==query(str2,mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans+1;
}

int main()
{
    freopen("1014.in","r",stdin);
    v[0]=1;
    for(int i=1;i<=150000;i++)v[i]=v[i-1]*27;
    int m,x,y;
    scanf("%s",s+2);
    size=strlen(s+2)+2;
    build(1,size,0);
    scanf("%d",&m);
    root=(size+1)>>1;
    for(int i=1;i<=m;i++)
    {
        char ch;
        scanf("\n%c%d ",&ch,&x);
        if(ch=='Q')scanf("%d",&y),printf("%d\n",ask(x,y));
        else if(ch=='R')
        {
            scanf("%c",&ch);
            int ans1=find(root,x+1);splay(ans1,root);
            t[root].v=ch-'a'+1;
            update(root);
        }
        else if(ch=='I')
        {
            scanf("%c",&ch);
            int ans1=find(root,x+1),ans2=find(root,x+2);
            splay(ans1,root);splay(ans2,t[root].r);
            t[ans2].l=++size;t[size].v=t[size].h=ch-'a'+1,t[size].fa=ans2;t[size].size++;
            update(ans2);update(ans1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值