bzoj1014: [JSOI2008]火星人prefix

本文通过使用单模哈希与Splay树的数据结构解决了字符串操作问题,包括修改字符、插入字符及查询最长公共前缀。作者经历了从双模哈希到单模哈希的优化过程,并最终实现了在10秒内通过测试案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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

题解

  故事是这样的:一开始我用了双模哈希,交上去老TLE。

  我觉得应该不是常数的问题,于是各种调试,还是T。

  然后我又make了好多大数据,去hzwer.com下载了黄学长的代码和我的比较,发现黄学长总比我快那么一点点,看了黄学长的代码发现他是单模哈希。

  然后我交了黄学长的代码,发现他10s卡着过了!

  于是我就各种优化常数,优化。。。优化。。。。改成了模质数+自然溢出

  然而还是T。

  最后我愤怒的把程序改成了单模哈希。结果卡着10s过了。。。。。。汗

  真是无语。

  哦忘了写题解。其实就是用splay维护一个字符串序列。

  对于R操作,直接找那个点,splay到根,更改值。

  对于I操作,找到编号为x和x+1的点,在中间插入(这里写麻烦了,我用了pos和tag,其实一个size就能够替代)。然后update。

  对于Q操作,查询x和y的最长公共前缀。首先二分答案,然后判断就是直接用splay取出这两段区间的hash值,判断是否相等。

  表示:单模hash冲突的概率也这么小吗??以后不写双模了好不好?

代码

//splay+hash
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 110000
#define ll long long
#define P 4894651LL
#define root RT->lch
using namespace std;
struct node
{
	ll size, hash, pos, tag, c;
	node *lch, *rch, *f;
	node(){size=1;hash=pos=tag=c=0;lch=rch=f=0;}
}*RT, nd[maxn];
ll mi26[2][maxn], length, nodetot;
char s[maxn];
void reads(char *s)
{
	char c=getchar();
	ll cnt=0;
	while((c>='a' and c<='z') or c=='Q' or c=='R' or c=='I')s[cnt++]=c,c=getchar();
	s[cnt]=0;
}
ll read(ll x=0)
{
	char c=getchar();
	while(c<48 or c>58)c=getchar();
	while(c>=48 and c<=58)x=x*10+c-48,c=getchar();
	return x;
}
inline void pushdown(node *p)
{
	if(p==0)return;
	if(p->tag==0)return;
	p->pos+=p->tag;
	if(p->lch)p->lch->tag+=p->tag;
	if(p->rch)p->rch->tag+=p->tag;
	p->tag=0;
}
inline void update(node *p)
{
	if(p==0)return;
	ll hash1, hash2, len=p->rch?p->rch->size:0;
	hash1=p->lch?p->lch->hash:0, hash2=p->rch?p->rch->hash:0;
	p->hash=(hash1*mi26[0][len+1]+p->c*mi26[0][len]+hash2)%P;
	p->size=(p->lch?p->lch->size:0)+1+len;
}
void zig(node *x)
{
	node *y=x->f, *z=y->f;
	pushdown(y),pushdown(x);
	if(z->lch==y)z->lch=x;
	else z->rch=x;
	x->f=z;
	if(x->rch)x->rch->f=y;y->lch=x->rch;
	x->rch=y;y->f=x;
	update(y),update(x);
}
void zag(node *x)
{
	node *y=x->f, *z=y->f;
	pushdown(y),pushdown(x);
	if(z->lch==y)z->lch=x;
	else z->rch=x;
	x->f=z;
	if(x->lch)x->lch->f=y;y->rch=x->lch;
	x->lch=y;y->f=x;
	update(y),update(x);
}
void rotate(node *p){if(p->f->lch==p)zig(p);else zag(p);}
void splay(node *x, node *F)
{
	node *y, *z;
	while(x->f!=F)
	{
		y=x->f;z=y->f;
		if(z==F){rotate(x);break;}
		if((z->lch==y&&y->lch==x) || (z->rch==y&&y->rch==x))rotate(y),rotate(x);
		else rotate(x),rotate(x);
	}
}
node* find(node *x, ll num)
{
	pushdown(x);
	if(x->pos==num)return x;
	else if(x->pos<num)return find(x->rch,num);
	else return find(x->lch,num);
}
bool check(ll l1, ll r1, ll l2, ll r2)
{
	if(l1>r1)return true;
	ll h1, h2;
	node *p1, *p2;
	l1--,l2--;r1++,r2++;
	p1=find(root,l1),p2=find(root,r1);
	splay(p1,RT),splay(p2,p1);
	h1=p2->lch->hash;
	p1=find(root,l2),p2=find(root,r2);
	splay(p1,RT),splay(p2,p1);
	h2=p2->lch->hash;
	return h1==h2;
}
ll LCQ(ll x, ll y)
{
	ll l, r, mid;
	l=0,r=min(length-x+1,length-y+1),mid=(l+r+1)>>1;
	while(l<r)
	{
		if(check(x,x+mid-1,y,y+mid-1))l=mid;
		else r=mid-1;
		mid=(l+r+1)>>1;
	}
	return l;
}
void input()
{
	node *p, *p1, *p2;
	ll i;
	reads(s+1);
	length=strlen(s+1);
	
	RT=nd+nodetot++;
	
	p1=nd+nodetot++;
	p1->pos=0;p1->c=p1->hash=-1;
	RT->lch=p1;p1->f=RT;
	
	p2=nd+nodetot++;
	p2->pos=length+1;p2->c=p2->hash=-1;
	p1->rch=p2;p2->f=p1;
	mi26[0][0]=mi26[1][0]=1;
	for(i=1;i<maxn;i++)
		mi26[0][i]=mi26[0][i-1]*26%P,mi26[1][i]=mi26[1][i-1]*26;
	for(i=1;i<=length;i++)
	{
		p=nd+nodetot++;
		p->c=p->hash=s[i]-97;
		p->pos=i;
		splay(p1,RT);
		p2->lch=p;p->f=p2;
		update(p2),update(p1);
		p1=p;
	}
}
void solve()
{
	ll x, y, M;
	node *p1, *p2, *p;
	char d[3], type[3];
	M=read();
	while(M--)
	{
		reads(type);
		x=read();
		if(*type=='Q')
		{
			y=read();
			printf("%lld\n",LCQ(x,y));
		}
		else if(*type=='R')
		{
			reads(d);
			p=find(root,x);
			splay(p,RT);
			p->c=*d-97;
			update(p);
		}
		else
		{
			reads(d);
			p1=find(root,x),p2=find(root,x+1);
			p=nd+nodetot++;
			p->c=p->hash=*d-97;
			p->pos=x+1;
			splay(p1,RT),splay(p2,p1);
			p2->tag++;
			pushdown(p2);
			p2->lch=p;p->f=p2;
			update(p2),update(p1);
			length++;
		}
	}
}
int main()
{
	input();
	solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值