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

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=1014

【题意】

给定一个字符串,要求提供修改一个字符,插入一个字符,查询两个后缀LCP的功能。

【思路】

  splay维护字符串的哈希值。因为要提供区间,splay采用先查找后调整至根的写法。

一个结点的hash值为:

ch[0]->h * X^(ch[1]->s+1)+v * X^(ch[1]->s)+ch[1]->h

    对于一个询问每次二分长度,提取区间后比较hash值即可。

需要注意的是splay要提前在区间的左右两边各加上一个节点,不然会调用到null。

ull自然溢出相当于模2^64。

【代码】

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 using namespace std;
 12
 13 typedef long long ll;
 14 typedef unsigned long long ull;
 15 const int N = 5e5+10;
 16 const int X = 27;
 17
 18 ll read() {
 19     char c=getchar();
 20     ll f=1,x=0;
 21     while(!isdigit(c)) {
 22         if(c==‘-‘) f=-1; c=getchar();
 23     }
 24     while(isdigit(c))
 25         x=x*10+c-‘0‘,c=getchar();
 26     return x*f;
 27 }
 28
 29 char s[N];
 30 int n,q;
 31 ull powx[N];
 32
 33 struct Node* null;
 34 struct Node {
 35     int s,v; ull h;
 36     Node* ch[2];
 37     int cmp(int k) {
 38         if(k==ch[0]->s+1) return -1;
 39         return k<=ch[0]->s? 0:1;
 40     }
 41     void init(int x) {
 42         v=h=x; s=1;
 43         ch[0]=ch[1]=null;
 44     }
 45     void maintain() {
 46         s=ch[0]->s+ch[1]->s+1;
 47         h=ch[0]->h*powx[ch[1]->s+1]+v*powx[ch[1]->s]+ch[1]->h;
 48     }
 49 } *root,nodepool[N]; int nodesz=0;
 50
 51 void rot(Node* &o,int d) {
 52     Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
 53     o->maintain(),k->maintain(); o=k;
 54 }
 55 void splay(Node*& o,int k) {
 56     int d=o->cmp(k);
 57     if(d==1) k-=o->ch[0]->s+1;
 58     if(d!=-1) {
 59         Node* p=o->ch[d];
 60         int d2=p->cmp(k),k2=(d2==0?k:k-p->ch[0]->s-1);
 61         if(d2!=-1) {
 62             splay(p->ch[d2],k2);
 63             if(d==d2) rot(o,d^1); else rot(o->ch[d],d);
 64         }
 65         rot(o,d^1);
 66     }
 67 }
 68 //return range (l,r]
 69 //加过点后 s[l,r]=range(l,r+1)
 70 Node*& range(int l,int r) {
 71     splay(root,l);
 72     splay(root->ch[1],r-l+1);
 73     return root->ch[1]->ch[0];
 74 }
 75
 76 Node* build(int l,int r)
 77 {
 78     if(r<l) return null;
 79     int mid=l+r>>1;
 80     Node* o=&nodepool[++nodesz];
 81     o->init(s[mid]-‘a‘+1);
 82     o->ch[0]=build(l,mid-1);
 83     o->ch[1]=build(mid+1,r);
 84     o->maintain();
 85     return o;
 86 }
 87 void insert(int p,int v)
 88 {
 89     splay(root,p+1);
 90     Node* o=&nodepool[++nodesz];
 91     o->init(v);
 92     o->ch[0]=root->ch[0]; o->ch[1]=null;
 93     o->maintain();
 94     root->ch[0]=o; root->maintain();
 95 }
 96 void change(int p,int v)
 97 {
 98     splay(root,p);
 99     root->v=v;
100     root->maintain();
101 }
102
103 int main()
104 {
105     //freopen("in.in","r",stdin);
106     //freopen("out.out","w",stdout);
107     null=new Node();
108     scanf("%s",s+1);
109     int n=strlen(s+1);
110     s[0]=‘z‘+1; s[++n]=‘z‘+1; s[n+1]=‘\0‘;
111     scanf("%d",&q);
112     powx[0]=1;
113     FOR(i,1,n+q) powx[i]=powx[i-1]*X;
114     root=build(0,n);
115     while(q--) {
116         char op[2],val[2];
117         int x,y;
118         scanf("%s%d",op,&x);
119         if(op[0]==‘R‘) {
120             scanf("%s",val);
121             change(x+1,val[0]-‘a‘+1);
122         } else
123         if(op[0]==‘I‘) {
124             scanf("%s",val);
125             insert(x+1,val[0]-‘a‘+1);
126         } else {
127             scanf("%d",&y);
128             int len=root->s,L=0,R=0;
129             R=min(len-y-1,len-x-1);
130             while(L<R) {
131                 int M=L+(R-L+1)/2;
132                 ull H=range(x,x+M)->h;
133                 H-=range(y,y+M)->h;
134                 if(!H) L=M; else R=M-1;
135             }
136             printf("%d\n",L);
137         }
138
139     }
140     return 0;
141 }
时间: 2024-10-06 09:51:32

bzoj 1014 [JSOI2008]火星人prefix(splay+hash)的相关文章

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 Splay+Hash+二分

题目大意:给定一个字符串,提供下列操作: 1.查询从x开始的后缀和从y开始的后缀的最长公共前缀长度 2.将x位置的字符修改为y 3.在x位置的字符后面插入字符y 看到这题一开始我先懵住了...这啥..我第一时间想到的是后缀数据结构 但是不会写 而且后缀数据结构也不支持修改操作 后来无奈找了题解才知道是Hash+二分... 太强大了 Hash+二分打爆一切啊 用Splay维护这个字符串的修改和插入操作 每个节点维护子串的Hash值 判断时二分找到最长公共前缀 不过这道题还有一些注意事项 1.此题不

bzoj 1014[JSOI2008]火星人prefix - 二分 + hash + splay

我们发现要支持修改操作,所以后缀数组就不适用了 查询两个字符串的lcp有两个很常见的算法, 后缀数组和 二分哈希 所以对于字符串的修改我们用一个splay 来维护, 平衡树每个节点表示的是对应子树的字符串的哈希值. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define LL long long 6 using namesp

bzoj 1014 [JSOI2008]火星人prefix (splay+二分答案+字符串hash)

题目大意:维护一个字符串,支持插入字符和替换字符的操作,以及查询该字符串两个后缀的最长公共前缀长度 乍一看以为是后缀数组,然而并没有可持久化后缀数组(雾) 看题解才知道这是一道splay题,首先要对splay维护区间信息有一定了解 splay维护,插入字符,替换字符 而它的字树内所有儿子的中序遍历的hash值也可以通过splay维护  (这个推导式似乎烂大街了) 而后缀就是把i-1拎到根节点,然后把n+1拎到根节点的右儿子上,它的左儿子表示的就是hash值 至于如何查公共前缀呢?二分答案啊!询问

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

1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4164  Solved: 1277[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】

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

【bzoj1014】[JSOI2008]火星人prefix Splay+Hash+二分

题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串: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

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

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(