1014: [JSOI2008]火星人prefix
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 6031 Solved: 1917
[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 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、询问。语法:Qxy,x,y均为正整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字
符串长度。
3、插入:语法:Ixd,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
1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。
【题解】
用splay维护字符串,把一段子串用哈希值表示,两个长度相同的子串如果哈希值相同,那么它们就相同。
建树时每棵子树的哈希值集中在根节点上,即h[x]=(h[left[x]]+v[x]*p[size[left]]+h[r]*p[size[left]+1])%mod
查询时二分一个答案,然后验证。
那么如何找到子串的哈希值呢?
首先找到子串的首字母,把它伸展到根,然后找到子串的尾字母的下一个字母,把它伸展到根的右儿子,那么根的右儿子的左儿子的哈希值就是答案。
吐槽:这题交了很多遍,都是RE,然后我才知道BZOJ上用cin会RE......
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<ctime> 7 #include<algorithm> 8 using namespace std; 9 #define MAXN 150010 10 #define ll long long 11 #define mod 9875321 12 int n,m,rt,sz,p[MAXN],c[MAXN][2],fa[MAXN],size[MAXN],h[MAXN],v[MAXN]; 13 char ch[MAXN]; 14 namespace INIT 15 { 16 inline int read() 17 { 18 int x=0,f=1; char ch=getchar(); 19 while(!isdigit(ch)) {if(ch==‘-‘) f=-1; ch=getchar();} 20 while(isdigit(ch)) {x=x*10+ch-‘0‘; ch=getchar();} 21 return x*f; 22 } 23 }using namespace INIT; 24 namespace SPLAY 25 { 26 void updata(int x) 27 { 28 int l=c[x][0],r=c[x][1]; 29 size[x]=size[l]+size[r]+1; 30 h[x]=h[l]+(ll)v[x]*p[size[l]]%mod+(ll)p[size[l]+1]*h[r]%mod; 31 h[x]%=mod; 32 } 33 void rotate(int x,int &k) 34 { 35 int y=fa[x],z=fa[y],l,r; 36 if(c[y][0]==x) l=0; else l=1; r=l^1; 37 if(y==k) k=x; 38 else {if(c[z][0]==y) c[z][0]=x; else c[z][1]=x;} 39 fa[x]=z; fa[y]=x; fa[c[x][r]]=y; 40 c[y][l]=c[x][r]; c[x][r]=y; 41 updata(y); updata(x); 42 } 43 void Splay(int x,int &k) 44 { 45 while(x!=k) 46 { 47 int y=fa[x],z=fa[y]; 48 if(y!=k) 49 { 50 if((c[y][0]==x)^(c[z][0]==y)) rotate(x,k); 51 else rotate(y,k); 52 } 53 rotate(x,k); 54 } 55 } 56 void build(int k,int l,int r) 57 { 58 if(l>r) return; 59 if(l==r) 60 { 61 v[l]=h[l]=ch[l]-‘a‘+1; 62 fa[l]=k; size[l]=1; 63 if(l<k) c[k][0]=l; 64 else c[k][1]=l; 65 return; 66 } 67 int mid=(l+r)/2; 68 build(mid,l,mid-1); build(mid,mid+1,r); 69 v[mid]=ch[mid]-‘a‘+1; fa[mid]=k; updata(mid); 70 if(mid<k) c[k][0]=mid; 71 else c[k][1]=mid; 72 } 73 int find(int k,int rank) 74 { 75 int l=c[k][0],r=c[k][1]; 76 if(size[l]+1==rank) return k; 77 else if(size[l]>=rank) return find(l,rank); 78 else return find(r,rank-size[l]-1); 79 } 80 int check(int k,int val) 81 { 82 int x=find(rt,k),y=find(rt,k+val+1); 83 Splay(x,rt); Splay(y,c[x][1]); 84 int z=c[y][0]; 85 return h[z]; 86 } 87 int ask(int x,int y) 88 { 89 int l=1,r=min(sz-x,sz-y)-1,ans=0; 90 while(l<=r) 91 { 92 int mid=(l+r)/2; 93 if(check(x,mid)==check(y,mid)) {l=mid+1; ans=mid;} 94 else r=mid-1; 95 } 96 return ans; 97 } 98 void insert(int k,int val) 99 { 100 int x=find(rt,k+1),y=find(rt,k+2); 101 Splay(x,rt); Splay(y,c[x][1]); 102 int z=++sz; c[y][0]=z; fa[z]=y; v[z]=val; 103 updata(z); updata(y); updata(x); 104 } 105 }using namespace SPLAY; 106 int main() 107 { 108 //freopen("cin.in","r",stdin); 109 //freopen("cout.out","w",stdout); 110 scanf("%s",ch+2); 111 n=strlen(ch+2); p[0]=1; 112 for(int i=1;i<=MAXN;i++) p[i]=p[i-1]*27%mod; 113 build(0,1,n+2); rt=(n+3)/2; sz=n+2; 114 m=read(); int x,y; 115 char s[2],d[2]; 116 for(int i=1;i<=m;i++) 117 { 118 scanf("%s",s+1); x=read(); 119 if(s[1]==‘Q‘) {y=read(); printf("%d\n",ask(x,y));} 120 if(s[1]==‘R‘) {scanf("%s",d+1); x=find(rt,x+1); Splay(x,rt); v[rt]=d[1]-‘a‘+1; updata(rt);} 121 if(s[1]==‘I‘) {scanf("%s",d+1); insert(x,d[1]-‘a‘+1);} 122 } 123 return 0; 124 }
附makedata程序(博主很良心,知道你会wa):
1 #include<iostream> 2 using namespace std; 3 char ch[5]={‘Q‘,‘R‘,‘I‘}; 4 int main() 5 { 6 freopen("cin.in","w",stdout); 7 srand(time(NULL)); 8 int n=100; 9 for(int i=1;i<=n;i++) printf("%c",(char)(‘a‘+rand()%26)); 10 printf("\n"); 11 int m=1500; 12 printf("%d\n",m); 13 for(int i=1;i<=m;i++) 14 { 15 char f=ch[rand()%3]; 16 if(f==‘Q‘) {printf("%c %d %d\n",f,rand()%n+1,rand()%n+1);} 17 if(f==‘R‘) {printf("%c %d %c\n",f,rand()%n+1,(char)(‘a‘+rand()%26));} 18 if(f==‘I‘) {printf("%c %d %c\n",f,rand()%n+1,(char)(‘a‘+rand()%26));} 19 } 20 return 0; 21 }