BZOJ 1014 [火星人 prefix]

题面

题意

  维护一个串,支持插入一个字符,修改一个字符,以及求两个后缀的最长公共前缀。

题解

  用平衡树维护字符串,每一个节点额外维护节点的子树代表字符串的哈希值。在写这道题时选择了 Splay 作为平衡树的实现方式,此时维护节点信息只需要在每一次旋转的时候更新关联节点子树的哈希值就可以保证正确。

  字符串的哈希方程使用 \(hash(s)=(\sum_{i=1}^{|s|}(id(s_i)-96)\cdot 31^{|s|-i}) \mod 2^{64}\)(其中 \(96\) 是 'a' 的 ASCII 码 \(-1\)\(31\)\(2^{64}\) 可以是任何不容易产生冲突的乘数和模数),既可以简洁地用左右子树的哈希值求出树的哈希值,也可以通过 \(hash(s_{i \dots j})=hash(s_{1 \dots j})-31^{j-i+1} \cdot hash(s_{1 \dots i-1}) \mod 2^{64}\) 用字符串的前缀哈希值求出字符串任意子串的哈希值,而求字符串的前缀哈希值时只需要把前缀后的第一个字符旋转到 Splay 树的根节点,此时根节点左子树的哈希值就是前缀的哈希值。为了防止在求整个字符串的哈希值时出错,额外在字符串结尾加入了一个 '$' 字符。

  插入和修改操作只需要把相关节点旋转到根节点,进行一次修改之后更新有关联的节点子树哈希值。求最长公共前缀时可以二分答案,每次检查对应的两个子串 \(hash(s_{x \dots x+m}),hash(s_{y \dots y+m})\) 是否相同。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef unsigned long long ull;
const int maxn=1e5+5;
const ull mul=31;
vector<ull> powarr;
ull getpow(int n){
    while (powarr.size()<=n)
        powarr.push_back(powarr.back()*mul);
    return powarr[n];
}
struct Splay_Tree{
    struct node{
        node *fa,*ch[2];
        char val;
        int size;
        ull hash;
    };
    node *root;
    node *const nil;
    node *nnode(char ch){
        node *nd=new node();
        nd->fa=nd->ch[0]=nd->ch[1]=nil;
        nd->val=ch; nd->size=1; nd->hash=nd->val-'a'+1;
        return nd;
    }
    Splay_Tree():nil(new node()){
        nil->fa=nil->ch[0]=nil->ch[1]=nil;
        nil->val=0; nil->size=0; nil->hash=0;
        root=nnode('$');
    }
    int size(){ return root->size-1; }
    void update(node *nd){
        nd->size=0; nd->hash=0;
        nd->size+=nd->ch[0]->size;
        nd->hash*=getpow(nd->ch[0]->size);
        nd->hash+=nd->ch[0]->hash;
        nd->size+=1;
        nd->hash*=mul;
        nd->hash+=nd->val-'a'+1;
        nd->size+=nd->ch[1]->size;
        nd->hash*=getpow(nd->ch[1]->size);
        nd->hash+=nd->ch[1]->hash;
    }
    bool getid(node *u){
        return u==u->fa->ch[1];
    }
    void rotate(node *u){
        node *v,*w,*x;
        bool b,c;
        if (u==nil) return;
        v=u->fa;
        if (v==nil) return;
        b=getid(u); c=getid(v);
        w=v->fa; x=u->ch[!b];
        v->ch[b]=x; if (x!=nil) x->fa=v;
        u->ch[!b]=v; v->fa=u;
        if (w!=nil) w->ch[c]=u; u->fa=w;
        update(v); update(u);
    }
    void splay(node *u){
        while (u->fa->fa!=nil){
            if (getid(u)==getid(u->fa)){
                rotate(u->fa); rotate(u);
            }
            else {
                rotate(u); rotate(u->fa);
            }
        }
        if (u->fa!=nil) rotate(u);
        root=u;
    }
    void find(int x){
        node *u=root;
        bool b;
        while (true){
            b=x>u->ch[0]->size;
            x-=b?u->ch[0]->size+1:0;
            if (!x) break;
            u=u->ch[b];
        }
        splay(u);
    }
    ull query(int x){
        find(x+1);
        return root->ch[0]->hash;
    }
    ull hash(int x,int y){
        return query(y)-query(x)*getpow(y-x);
    }
    void change(int x,char ch){
        find(x);
        root->val=ch;
        update(root);
    }
    void insert(int x,char ch){
        find(x+1);
        node *u,*v,*w;
        u=nnode(ch);
        v=root->ch[0];
        w=root;
        u->ch[0]=v;
        if (v!=nil) v->fa=u;
        u->ch[1]=w;
        w->fa=u;
        w->ch[0]=nil;
        update(w);
        update(u);
        root=u;
    }
};
Splay_Tree tree;
char s[maxn],a[10],b[10];
int query(int x,int y){
    int l,r,m;
    l=0; r=tree.size()-y+2;
    while (r-l>1){
        m=(l+r)/2;
        if (tree.hash(x-1,x+m-1)==tree.hash(y-1,y+m-1))
            l=m;
        else r=m;
    }
    return l;
}
int main(){
    int i,m,x,y;
    powarr.clear(); powarr.push_back(1);
    scanf("%s",s);
    for (i=strlen(s)-1;i>=0;i--)
        tree.insert(0,s[i]);
    scanf("%d",&m);
    while (m--){
        scanf("%s",a);
        switch (a[0]){
        case 'Q':
            scanf("%d%d",&x,&y);
            if (x>y) swap(x,y);
            printf("%d\n",query(x,y));
            break;
        case 'R':
            scanf("%d%s",&x,b);
            tree.change(x,b[0]);
            break;
        case 'I':
            scanf("%d%s",&x,b);
            tree.insert(x,b[0]);
            break;
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Kilo-5723/p/12189932.html