首页 > 代码库 > BZOJ 1014 JSOI2008 火星人prefix Splay+Hash+二分

BZOJ 1014 JSOI2008 火星人prefix Splay+Hash+二分

题目大意:给定一个字符串,提供下列操作:

1.查询从x开始的后缀和从y开始的后缀的最长公共前缀长度

2.将x位置的字符修改为y

3.在x位置的字符后面插入字符y

看到这题一开始我先懵住了。。。这啥。。我第一时间想到的是后缀数据结构 但是不会写 而且后缀数据结构也不支持修改操作

后来无奈找了题解才知道是Hash+二分。。。 太强大了 Hash+二分打爆一切啊

用Splay维护这个字符串的修改和插入操作 每个节点维护子串的Hash值 判断时二分找到最长公共前缀

不过这道题还有一些注意事项

1.此题不卡Hash 不会出现ABBABAABBAABABBA之类恶心字符串

2.题目没有明说 但是字符串上所有字符都是小写字母

3.二分时注意上界的取值 不能超过字符串长度 此题不保证x<y 一定要特判 否则就会RE

剩下就很裸了、、、80%达成 可以补个番了0.0

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
using namespace std;
typedef unsigned long long ll;
struct abcd{
	abcd *fa,*ls,*rs;
	int num,siz;ll hash;
	abcd(int x);
	void Push_Up();
}*null=new abcd(0),*root=null;
abcd :: abcd(int x)
{
	fa=ls=rs=null;
	num=x;siz=x?1:0;hash=x;
}
ll ksm[M];
void abcd :: Push_Up()
{
	siz=ls->siz+rs->siz+1;
	hash=ls->hash*ksm[rs->siz+1]+num*ksm[rs->siz]+rs->hash;
}
void Zig(abcd *x)
{
	abcd *y=x->fa;
	y->ls=x->rs;
	x->rs->fa=y;
	x->rs=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
	if(y==root)
		root=x;
}
void Zag(abcd *x)
{
	abcd *y=x->fa;
	y->rs=x->ls;
	x->ls->fa=y;
	x->ls=y;
	x->fa=y->fa;
	if(y==y->fa->ls)
		y->fa->ls=x;
	else if(y==y->fa->rs)
		y->fa->rs=x;
	y->fa=x;
	y->Push_Up();
	if(y==root)
		root=x;
}
void Splay(abcd *x,abcd *Tar)
{
	while(1)
	{
		abcd *y=x->fa,*z=y->fa;
		if(y==Tar)
			break;
		if(z==Tar)
		{
			if(x==y->ls)
				Zig(x);
			else
				Zag(x);
			break;
		}
		if(x==y->ls)
		{
			if(y==z->ls)
				Zig(y);
			Zig(x);
		}
		else
		{
			if(y==z->rs)
				Zag(y);
			Zag(x);
		}
	}
	x->Push_Up();
}
void Find(abcd *x,int y,abcd *z)
{
	while(1)
	{
		if(y<=x->ls->siz)
			x=x->ls;
		else
		{
			y-=x->ls->siz;
			if(y==1)
				break;
			y--;
			x=x->rs;
		}
	}
	Splay(x,z);
}
char s[M];
void Build_Tree(abcd *&x,int l,int r)
{
	if(l>r)
		return ;
	int mid=l+r>>1;
	x=new abcd(s[mid]-'a'+1);
	Build_Tree(x->ls,l,mid-1);
	Build_Tree(x->rs,mid+1,r);
	x->ls->fa=x;
	x->rs->fa=x;
	x->Push_Up();
}
void Initialize()
{
	int i;
	ksm[0]=1;
	for(i=1;i<=100010;i++)
		ksm[i]=ksm[i-1]*31;
	scanf("%s",s);
	root=new abcd(19980402);
	root->rs=new abcd(19980402);
	Build_Tree(root->rs->ls,0,strlen(s)-1);
	root->rs->ls->fa=root->rs;
	root->rs->fa=root;
	root->rs->Push_Up();
	root->Push_Up();
}
bool Judge(int x,int y,int ans)
{
	Find(root,x,null);
	Find(root,x+ans+1,root);
	ll hash1=root->rs->ls->hash;
	Find(root,y,null);
	Find(root,y+ans+1,root);
	ll hash2=root->rs->ls->hash;
	return hash1==hash2;
}
int Query(int x,int y)
{
	int l=0,r=root->siz-2-max(x,y)+1;
	while(l+1<r)
	{
		int mid=l+r>>1;
		if( Judge(x,y,mid) )
			l=mid;
		else
			r=mid;
	}
	if( Judge(x,y,r) )
		return r;
	return l;
	
}
int m;
int main()
{
	int i,x,y;
	char p[10];
	Initialize();
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%s",p);
		if(p[0]=='R')
		{
			scanf("%d%s",&x,p);
			Find(root,x+1,null);
			root->num=p[0]-'a'+1;
			root->Push_Up();
		}
		else if(p[0]=='I')
		{
			scanf("%d%s",&x,p);
			Find(root,x+1,null);
			Find(root,x+2,root);
			root->rs->ls=new abcd(p[0]-'a'+1);
			root->rs->ls->fa=root->rs;
			root->rs->Push_Up();
			root->Push_Up();
		}
		else
			scanf("%d%d",&x,&y),printf("%d\n", Query(x,y) );
	}
}



BZOJ 1014 JSOI2008 火星人prefix Splay+Hash+二分