[JSOI2008 Prefix火星人]

[关键字]:splay hash 二分

[题目大意]:给出一个字符串,求出给定的两个后缀的的最长公共前缀。在求的过程中会有改变或在某个位置添加字符的操作。

//============================================================================================

[分析]:一听最长公共前缀马上想到后缀数组,但因为是动态维护所以后缀数组也无能为力。可以把字符串想象成一个数组,于是变成了动态插入和改变一个序列,还要能快速找到两个子区间——splay。求最长公共前缀可以用二分答案加验证的方法,二分长度先提取出这个区间然后判断这个区间的根节点的hash值(实际就是这棵子树的hash)是否和另一个相同。hash值得判断可以用进制法:hash=ord(s[1])*270+ord(s[2])*271+……+ord(s[n])*27n-1,在旋转时更新hash值为:hash[lc]+s[v]*27^(size[lc]+1)+hash[rc]*27^(size[lc]+2)。插入操作和一般splay插入无异修改只要修改掉这个点的值和hash值就行了。

[代码]:

View Code
#include<iostream>

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<algorithm>

using namespace std;



const int MAXL=100015;

const int MOD=9875321;



struct node

{

    node *c[2],*f;

    int dat,size,hash;

}*root,SC[MAXL],*null;

char s[MAXL];

int m,SS;

int d[MAXL];



void Debug(node *v)

{

    if (v==null) return;

    Debug(v->c[0]);

    printf("%d ",v->dat);

    Debug(v->c[1]);

}



node *New(int d,int h,node *fa)

{

    node *e=SC+ ++SS;

    e->size=1,e->f=fa;

    e->c[1]=e->c[0]=null;

    e->dat=d,e->hash=h;

    return e;

}



void Update(node *v)

{

    if (v==null) return;

    v->size=v->c[0]->size+v->c[1]->size+1;

    v->hash=(long long)(v->c[0]->hash+(long long)v->dat*d[(v->c[0]->size+1)]+(long long)v->c[1]->hash*d[(v->c[0]->size+2)])%MOD;

}



void Prepare()

{

    SS=-1;

    null=NULL;

    null=New(0,0,NULL);

    null->size=0;

    root=New(0,0,null);

    root->c[1]=New(0,0,root);

    d[1]=1;

    for (int i=2;i<=100010;++i)

        d[i]=(d[i-1]*27)%MOD;

    Update(root);

}



void Rotate(node *x,int o)

{

    node *y=x->f;

    y->c[o]=x->c[!o];

    y->c[o]->f=y;

    x->f=y->f;

    if (y->f->c[0]==y)

        y->f->c[0]=x;

    else

        y->f->c[1]=x;

    x->c[!o]=y;

    y->f=x;

    if (y==root) root=x;

    Update(y);

}



void Splay(node *x,node *fa)

{

    while (x->f!=fa)

        if (x->f->f==fa)

            if (x->f->c[0]==x)

                Rotate(x,0);

            else

                Rotate(x,1);

        else

            if (x->f->f->c[0]==x->f)

                if (x->f->c[0]==x)

                    Rotate(x->f,0),Rotate(x,0);

                else

                    Rotate(x,1),Rotate(x,0);

            else    

                if (x->f->c[1]==x)

                    Rotate(x->f,1),Rotate(x,1);

                else

                    Rotate(x,0),Rotate(x,1);

    Update(x);

}



void Select(int k,node *fa)

{

    node *t=root;

    while (1)

    {

        if (k==t->c[0]->size+1) break;

        if (k<t->c[0]->size+1) t=t->c[0];

        if (k>t->c[0]->size+1) k-=t->c[0]->size+1,t=t->c[1];

    }

    Splay(t,fa);

}



int Hash(int x,int y)

{

    Select(x,null),Select(y+2,root);

    return root->c[1]->c[0]->hash;

}



void Ask(int x,int y)

{

    int ans=0,n=root->size-2;

    for (int i=1<<18;i;i>>=1)

        if (x+i-1<=n && y+i-1<=n && Hash(x,x+i-1)==Hash(y,y+i-1))

            ans+=i,x+=i,y+=i;

    printf("%d\n",ans);

}



void Ins(int x,char ch)

{

    Select(x+1,null),Select(x+2,root);

    root->c[1]->c[0]=New(ch-'a',ch-'a',root->c[1]);

    Splay(root->c[1]->c[0],null);

    //Debug(root),printf("\n");

}



void Rep(int x,char ch)

{

    Select(x,null),Select(x+2,root);

    node *v=root->c[1]->c[0];

    v->dat=ch-'a';

    v->hash=(long long)(v->c[0]->hash+(long long)v->dat*d[(v->c[0]->size+1)]+(long long)v->c[1]->hash*d[(v->c[0]->size+2)])%MOD;

    //Debug(root),printf("\n");

}



void Init()

{

    node *z,*t;

    Prepare();

    scanf("%s",s);

    int n=strlen(s);

    z=t=New(s[0]-'a',s[0]-'a',null);

    for (int i=1;i<n;++i)

        z=z->c[1]=New(s[i]-'a',s[i]-'a',z);

    root->c[1]->c[0]=t,t->f=root->c[1];

    Splay(z,null);

    //Debug(root),printf("\n");

}



void Solve()

{

    char ch; int x,y;

    scanf("%d",&m),scanf("%c",&ch);

    for (int i=1;i<=m;++i)

    {

        scanf("%c",&ch);

        if (ch=='Q') scanf("%d %d",&x,&y),Ask(x,y); else

        if (ch=='I') scanf("%d %c",&x,&ch),Ins(x,ch); else

            scanf("%d %c",&x,&ch),Rep(x,ch);

        scanf("%c",&ch);

    }

}



int main()

{

    freopen("in","r",stdin);

    freopen("out","w",stdout);

    Init();

    Solve();

    return 0;

}

你可能感兴趣的:(2008)