JSOI2008 火星人

12 篇文章 0 订阅
3 篇文章 0 订阅

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

splay上做Rabin-Karp(字符串哈希)。本身是个水题,但被我做成了毒瘤题= =调了一个晚自习。。。

前半个小时:主体程序

前1个半小时:发现没有初始化

前2个半小时:LCQ(x,y)的x+len可能超过y(没有考虑)

前3个半小时:x可能大于y。。。

#include<bits/stdc++.h>
using namespace std;
const int e1=31,p1=998244353,e2=131,p2=1e9+7;
const int Maxn=100005;
int f1[Maxn]={1},f2[Maxn]={1};
char s[Maxn];
int n;
struct Splay{
	int v[Maxn],h1[Maxn],h2[Maxn];
	int p[Maxn],siz[Maxn],ch[Maxn][2];
	int cnt,root;
	#define ls(x) ch[x][0]
	#define rs(x) ch[x][1]
	inline void newnode(int x,int Fa,char alpha){
		p[x]=Fa;
		siz[x]=1;
		ls(x)=rs(x)=0;
		v[x]=h1[x]=h2[x]=alpha-'a';
	}
	inline void maintain(int x){
		h1[x]=(1ll*h1[ls(x)]*f1[siz[rs(x)]+1]%p1+1ll*v[x]*f1[siz[rs(x)]]%p1+h1[rs(x)]%p1)%p1;
		h2[x]=(1ll*h2[ls(x)]*f2[siz[rs(x)]+1]%p2+1ll*v[x]*f2[siz[rs(x)]]%p2+h2[rs(x)]%p2)%p2;
		siz[x]=siz[ls(x)]+siz[rs(x)]+1;
	}
	inline void rotate(int x){
		int f=p[x],gf=p[f],tp=rs(f)==x,son=ch[x][!tp];
		ch[p[son]=f][tp]=son,maintain(f);
		ch[p[f]=x][!tp]=f,maintain(x);
		ch[p[x]=gf][rs(gf)==f]=x;
	}
	inline void splay(int x,int goal){
		if(x==goal)return ;
		for(;p[x]^goal;rotate(x))
			if((p[p[x]]^goal)&&((rs(p[p[x]])==p[x])==(rs(p[x])==x)))rotate(p[x]);
		if(!goal)root=x;
	}
	inline void build(int &x,int l,int r,int fr){
		if(l>r)return ;
		int mid=l+r>>1;
		newnode(x=++cnt,fr,s[mid]);
		build(ls(x),l,mid-1,x),build(rs(x),mid+1,r,x);
		maintain(x);
	}
	inline int findpos(int k){
		for(int x=root;;k>siz[ls(x)]+1?k-=siz[ls(x)]+1,x=rs(x):x=ls(x))
			if(k==siz[ls(x)]+1)return x;
	}
	inline pair<int,int> query(int l,int r){
		int pre=findpos(l-1),sub=findpos(r+1);
		splay(pre,0),splay(sub,pre);
		return make_pair(h1[ls(sub)],h2[ls(sub)]);
	}
	inline void modify(int pos,char alpha){
		int x=findpos(pos),pre=findpos(pos-1),sub=findpos(pos+1);
		splay(pre,0),splay(sub,pre);newnode(x,sub,alpha);splay(x,0);
	}
	inline void insert(int pos,char alpha){//插在pos位置后
		int pre=findpos(pos),sub=findpos(pos+1);
		splay(pre,0),splay(sub,pre);
		newnode(ls(sub)=++cnt,sub,alpha);
		maintain(sub);maintain(pre);
	}
}splay;
inline void query(){
	int x,y;scanf("%d%d",&x,&y);
	int l=1,r=min(n-x+1,n-y+1),ans=0;
	while(l<=r){
		int mid=l+r>>1;
		if(splay.query(x+1,x+mid)==splay.query(y+1,y+mid))ans=mid,l=mid+1;
		else r=mid-1;
	}
	cout<<ans<<"\n";
}
inline void modify(){
	int x;char c;scanf("%d %c",&x,&c);
	splay.modify(x+1,c);
}
inline void insert(){
	int x;char c;scanf("%d %c",&x,&c);
	splay.insert(x+1,c);++n;
}
int main(){
	for(int i=1;i<=1e5;++i)f1[i]=1ll*f1[i-1]*e1%p1;
	for(int i=1;i<=1e5;++i)f2[i]=1ll*f2[i-1]*e2%p2;
	scanf("%s",s+2);n=strlen(s+2);
	s[1]='z';s[strlen(s+1)+1]='z';
	splay.build(splay.root,1,strlen(s+1),0);
	int m;scanf("%d",&m);
	for(int i=1;i<=m;++i){
		char c=getchar();while(!isalpha(c))c=getchar();
		switch(c){
			case 'Q':query();break;
			case 'R':modify();break;
			case 'I':insert();break;
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值