【二分+hash】【字符串】【平衡树】【JSOI 2008】火星人prefix

1014: [JSOI2008]火星人prefix

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4264  Solved: 1306

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个数据,没有插入操作。

题解:

字符串hash。

经典的二分+hash,因为有插入和修改,用平衡树维护一下就好了

ps:下次一定要写splay。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200100

struct node{
    node *l,*r;
    int sz,cnt,p; unsigned int v;
    char c;
}*root,*null=new node((node){null,null,0,0,0,0,}),chi[250100];
int m,len,num=0; char s[N];
unsigned int mi[N];
typedef pair<node*,node*> Dnode;

inline int in(){
    int x=0; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
    return x;
}

inline void push_up(node *x){
    x->sz=x->l->sz+x->r->sz+x->cnt;
    x->v=(int)(x->c)-96;
    if (x->l->sz && !x->r->sz)
        x->v=x->l->v+x->v*mi[x->l->sz];
    else if (!x->l->sz && x->r->sz)
        x->v=x->v+x->r->v*mi[1];
    else if (x->l->sz && x->r->sz)
        x->v=x->l->v+x->v*mi[x->l->sz]+x->r->v*mi[x->l->sz+x->cnt];
}

inline node *merge(node *x,node *y){
    if (!x->sz) return y;
    if (!y->sz) return x;
    if (x->p<y->p){
        x->r=merge(x->r,y);
        push_up(x); return x;
    }
    else {
        y->l=merge(x,y->l);
        push_up(y); return y;
    }
}
inline Dnode split(node *x,int k){
    if (!x->sz) return Dnode(null,null);
    Dnode y;
    if (k<=x->l->sz){
        y=split(x->l,k); x->l=y.second;
        push_up(x); y.second=x;
    }
    else {
        y=split(x->r,k-x->l->sz-x->cnt);
        x->r=y.first; push_up(x); y.first=x;
    }
    return y;
}

inline void Insert(int w,char c){
    Dnode x=split(root,w);
    node *t=&chi[num++]; t->l=t->r=null;
    t->sz=t->cnt=1; t->c=c;
    t->v=(int)(c)-96; t->p=rand();
    x.first=merge(x.first,t);
    root=merge(x.first,x.second);
}
inline void Delete(int w){
    Dnode x,y;
    x=split(root,w-1);
    y=split(x.second,1);
    root=merge(x.first,y.second);
}
inline void query(int x,int y){
    int l=1,r=min(len-x+1,len-y+1),ans=0;
    unsigned int k1,k2; Dnode a,b;
    while (l<=r){
        int mid=(l+r)>>1;
        a=split(root,x-1),b=split(a.second,mid);
        push_up(b.first); k1=b.first->v;
        a.second=merge(b.first,b.second);
        root=merge(a.first,a.second);
        a=split(root,y-1),b=split(a.second,mid);
        push_up(b.first); k2=b.first->v;
        a.second=merge(b.first,b.second);
        root=merge(a.first,a.second);
        if (k1==k2) l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d\n",ans);
}

int main(){
    scanf("%s",s); len=strlen(s);
    m=in(); mi[0]=1; root=null;
    for (int i=1; i<=N; i++)
        mi[i]=mi[i-1]*29;
    node *x;
    for (int i=0; i<len; i++){
        x=&chi[num++]; x->cnt=x->sz=1;
        x->l=x->r=null; x->c=s[i];
        x->v=(int)(x->c)-96; x->p=rand();
        root=merge(root,x);
    }
    while (m--){
        char opt=getchar();
        while (opt!=‘Q‘ && opt!=‘R‘ && opt!=‘I‘) opt=getchar();
        int x,y; char c;
        switch (opt){
            case ‘Q‘:x=in(),y=in(); query(x,y); break;
            case ‘R‘:{
                x=in(),c=getchar();
                while (c<‘a‘ || c>‘z‘) c=getchar();
                Delete(x),Insert(x-1,c);
                break;
            }
            case ‘I‘:{
                x=in(),c=getchar();
                while (c<‘a‘ || c>‘z‘) c=getchar();
                Insert(x,c); len++;
                break;
            }
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-28 23:23:10

【二分+hash】【字符串】【平衡树】【JSOI 2008】火星人prefix的相关文章

JSOI 2008 火星人prefix

FROM http://www.lydsy.com/JudgeOnline/problem.php?id=1014 LCP问题 给定串 S[0..n] , 对于一对(a,b)其中0<a,b<n,求一个最大的k使得S[a..a+k]=S[b..b+k] 解决方法: Hash加二分 对于每个子串,我们都可以用基于多项式模大素数的hash函数进行判重. 静态LCP 静态LCP可以用DP二分解决.[详见 CQF `New LCP'] 动态LCP type1 查询居多,修改少. CQF解决方案. [详见

【BZOJ-1014】火星人prefix Splay + 二分 + Hash

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5852  Solved: 1871[Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d

BZOJ-1014 [JSOI2008]火星人prefix (Splay+二分+hash)

题解: 用splay维护添加修改操作,然后二分hash判断长度. 操作一:对于查询区间[l,r]的hash值,显然将l-1旋到根,将r+1旋到根的右儿子,此时所求区间就是根的右儿子的左儿子了. 操作二:将要修改的位置旋到根,然后直接改就可以了. 操作三:要在x后面添加一个字符,显然将x旋到根,x+1旋到根的右儿子,然后直接加在根的右儿子的左儿子上就可以了. 1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio&g

BZOJ 1014: [JSOI2008]火星人prefix( splay + hash )

用splay维护序列, 二分+hash来判断LCQ.. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 100009; const int P = 1000173169; ull K[maxn]; int N; char S[maxn]; struct Node { Node *ch[2], *p; int s, v; ull h; inline void

bzoj 1014: [JSOI2008]火星人prefix hash &amp;&amp; splay

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3154  Solved: 948[Submit][Status] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人

[BZOJ 1014] [JSOI2008] 火星人prefix 【Splay + Hash】

题目链接:BZOJ - 1014 题目分析 求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等. 这里有修改字符和插入字符的操作,所以用 Splay 来维护串的 Hash 值. 一个节点的值就是它的子树表示的字串的 Hash 值. 使用 unsigned long long 然后自然溢出就不需要 mod 了,速度会快很多. 代码 #include <iostream> #include <cstd

1014: [JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 7820  Solved: 2487 [Submit][Status][Discuss] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam, 我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a

【BZOJ】【1014】【JLOI2008】火星人prefix

Splay/二分/Hash 看了网上的题目关键字(都不用点进去看……我也是醉了)了解到做法= =那就上呗,前面做了好几道Splay的题就是为了练手搞这个的. Hash判断字符串是否相同应该很好理解吧?>_>我就不细说了 二分这个相同前缀的长度应该也容易>_> 用Splay维护这个Hash值>_>……也挺简单的,跟据size域就能算出以x为根的子树的hash值了. 这次我终于发现了一个之前以为不太重要的点……让我WA了两次= =!! 就是Splay在执行完序列插入的时候,

【BZOJ 1014】 [JSOI2008]火星人prefix

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 3471  Solved: 1065 [Submit][Status] Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,