uva11996 - Jewel Magic 伸展树

本文介绍了一种名为Jewel Magic的问题,涉及在一个包含0和1的字符串上进行插入、删除、反转等操作,并能快速回答关于最长公共前缀(LCP)的查询。通过建立伸展树并利用hash值来维护和比较字符串,可以高效地处理这些操作和查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem J

Jewel Magic

I am a magician. I have a string of emeralds and pearls. I may insert new jewels in the string, or remove old ones. I may even reverse a consecutive part of the string. At anytime, if you point to two jewels and ask me, what is the length of the longest common prefix (LCP) of jewel strings starting from these two jewels, I can answer your question instantly. Can you do better than me?

Formally, you'll be given a string of 0 and 1. You're to deal with four kinds of operations (in the following descriptions, L denotes the current length of the string, and jewel positions are number 1 to L numbered from left to right):

1 p c

Insert a jewel c after position p (0<=p<=L. p=0 means insert before the whole string). c=0 means emerald, c=1 represents pearl.

2 p

Remove the jewel at position p (1<=p<=L).

3 p1 p2

Reverse the part starting from position p1, ending at position p2 (1<=p1 < p2<=L)

4 p1 p2

Output the LCP length of jewel strings starting from p1 and p2 (1<=p1 < p2<=L).

Input

There will be several test cases. The first line of each test case contains an integer n and m (1<=n,m<=200,000), where n is the number of pearls initially, m is the number of operations. The next line contains a 01 string of length n. Each of the next m lines contains an operation. The input is terminated by end-of-file (EOF). The size of input file does not exceed 5MB.

Output

For each type-4 operation, output the answer.

Sample Input

12 9
000100001100
1 0 1
4 2 6
3 7 10
4 1 7
2 9
4 3 11
4 1 9
4 1 7
4 2 3

Output for the Sample Input

3
6
2
0
3
2

  长度为N的0,1串,四种操作。

  1 p c,在第p个字符之后插入字符c。

  2 p ,删除第p个字符,后面的字符往前移。

  3 p1 p2,反转第p1到p2个字符。

  4 p1 p2,输出p1开始和p2开始的两个后缀的LCP。


  首先建立虚拟开始结点和结束结点,root是开始结点,root->ch[1]是结束结点,以root->ch[1]->ch[0]为根建立01串的树,这个可以像线段树那样二分建立。对于1,2,3操作,都用range截出相应的一段操作就行了,对于4操作,可以二分长度L,然后截出(x,x+L)和(y,y+L),判断这两个串相不相等,用hash值来判断比较好,因此对于每个结点我们还需要h1和h2代表以这个结点为根的串的正向hash值和倒过来的hash值,维护的时候根据hash值的定义有h1=ch[0]->h1*powers[ch[1]->s+1]+v*powers[ch[1]->s]+ch[1]->h1,h2=ch[1]->h2*powers[ch[0]->s+1]+v*powers[ch[0]->s]+ch[0]->h2,这样我们只需要比较截出来的两段hash值是否相等,其实只用比较h1就行了,但为了防止冲突发生,把h1和h2都比较一下更保险。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<algorithm>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define MAXNODE 105
#define MOD 10000007
#define SIGMA_SIZE 4
typedef long long LL;
using namespace std;

const int MAXN=400020;
unsigned powers[MAXN];

struct Node *null,*pit;
struct Node{
    Node* ch[2];
    int s;
    int flip;
    int v;
    unsigned h1,h2;

    Node(){}
    Node(int v):s(1),flip(0),v(v),h1(v),h2(v){
        ch[0]=ch[1]=null;
    }
    void* operator new(size_t){
        return pit++;
    }
    int cmp(int k) const{
        int d=k-ch[0]->s;
        if(d==1) return -1;
        return d<=0?0:1;
    }
    void maintain(){
        s=ch[0]->s+ch[1]->s+1;
        h1=ch[0]->h1*powers[ch[1]->s+1]+v*powers[ch[1]->s]+ch[1]->h1;
        h2=ch[1]->h2*powers[ch[0]->s+1]+v*powers[ch[0]->s]+ch[0]->h2;
    }
    void reverse(){
        flip^=1;
        swap(ch[0],ch[1]);
        swap(h1,h2);
    }
    void pushdown(){
        if(flip){
            flip=0;
            ch[0]->reverse();
            ch[1]->reverse();
        }
    }
}pool[MAXN];

void init_null(){
    null=new Node();
    null->s=0;
}

void rotate(Node*& o,int d){
    Node* k=o->ch[d^1];
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}

//k>=1
void splay(Node*& o,int k){
    o->pushdown();
    int d=o->cmp(k);
    if(d==1) k-=o->ch[0]->s+1;
    if(d!=-1){
        Node* p=o->ch[d];
        p->pushdown();
        int d2=p->cmp(k);
        int k2=(d2==0?k:k-p->ch[0]->s-1);
        if(d2!=-1){
            splay(p->ch[d2],k2);
            if(d2==d) rotate(o,d^1);
            else rotate(o->ch[d],d);
        }
        rotate(o,d^1);
    }
}

struct SplaySequence{
    char* s;
    Node* root;

    // update dummy nodes
    //root: dummy min node
    // root->ch[1]: dummy max node
    // root->ch[1]->ch[0]: actual sequence
    void update_dummy(){
        root->ch[1]->maintain();
        root->maintain();
    }

    //[L,R)
    Node* build(int L,int R){
        int mid=L+(R-L)/2;
        Node* o=new Node(s[mid]);
        if(L<mid) o->ch[0]=build(L,mid);
        if(mid+1<R) o->ch[1]=build(mid+1,R);
        o->maintain();
        return o;
    }
    Node* build(char* s){
        this->s=s;
        root=new Node('[');
        root->ch[1]=new Node(']');
        root->ch[1]->ch[0]=build(0,strlen(s));
        update_dummy();
        return root;
    }

    //[L,R) L>=1
Node*& range(int L,int R){
    splay(root,L);
    splay(root->ch[1],R-L+1);
    return root->ch[1]->ch[0];
}

}ss;

int N,M;
char str[MAXN];

int main(){
    freopen("in.txt","r",stdin);
    powers[0]=1;
    for(int i=1;i<MAXN;i++) powers[i]=powers[i-1]*3137;
    while(scanf("%d%d%s",&N,&M,str)!=EOF){
        pit=pool;
        init_null();
        ss.build(str);
        int op,x,y;
        while(M--){
            scanf("%d%d",&op,&x);
            if(op==1){
                scanf("%d",&y);
                ss.range(x+1,x+1)=new Node(y+'0');
                ss.update_dummy();
            }
            else if(op==2){
                ss.range(x,x+1)=null;
                ss.update_dummy();
            }
            else if(op==3){
                scanf("%d",&y);
                ss.range(x,y+1)->reverse();
                ss.update_dummy();
            }
            else{
                scanf("%d",&y);
                //[L,R)
                int L=0,R=ss.root->s-y;
                while(L+1<R){
                    int mid=L+(R-L)/2;
                    unsigned h1=ss.range(x,x+mid)->h1;
                    if(h1==ss.range(y,y+mid)->h1) L=mid;
                    else R=mid;
                }
                printf("%d\n",L);
            }
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值