[BZOJ 1014] [JSOI2008] 火星人prefix 【Splay + Hash】

题目链接:BZOJ - 1014

 

题目分析

求两个串的 LCP ,一种常见的方法就是 二分+Hash,对于一个二分的长度 l,如果两个串的长度为 l 的前缀的Hash相等,就认为他们相等。

这里有修改字符和插入字符的操作,所以用 Splay 来维护串的 Hash 值。

一个节点的值就是它的子树表示的字串的 Hash 值。

使用 unsigned long long 然后自然溢出就不需要 mod 了,速度会快很多。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

inline int gmax(int a, int b) {return a > b ? a : b;}

typedef unsigned long long ULL;

const ULL Seed = 211;

const int MaxN = 100000 + 5;

int n, l, m, Root, Index;
int Father[MaxN], Son[MaxN][2], Size[MaxN];

ULL H[MaxN], Pow_Seed[MaxN]; 

char Str[MaxN], T[MaxN];

int NewNode(char c)
{
	int x = ++Index;
	T[x] = c;
	H[x] = (ULL)c;
	Size[x] = 1;
	return x;
}

inline void Update(int x)
{
	Size[x] = Size[Son[x][0]] + Size[Son[x][1]] + 1;
	H[x] = (H[Son[x][0]] * Seed + T[x]) * Pow_Seed[Size[Son[x][1]]] + H[Son[x][1]];
}

int Build(int s, int t)
{
	int x, m = (s + t) >> 1;
	x = NewNode(Str[m]);
	if (s < m) 
	{
		Son[x][0] = Build(s, m - 1);
		Father[Son[x][0]] = x;
	}
	if (t > m)
	{
		Son[x][1] = Build(m + 1, t);
		Father[Son[x][1]] = x;
	}
	Update(x);
	return x;		
}

inline int GetDir(int x)
{
	if (x == Son[Father[x]][0]) return 0;
	else return 1;
}

void Rotate(int x)
{
	int y = Father[x], f = GetDir(x) ^ 1;
	Father[x] = Father[y];
	if (Father[y])
	{
		if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
		else Son[Father[y]][1] = x;
	}
	Son[y][f ^ 1] = Son[x][f];
	if (Son[x][f]) Father[Son[x][f]] = y;
	Son[x][f] = y;
	Father[y] = x;
	Update(y); Update(x);
}

void Splay(int x, int d)
{
	int y;
	while (Father[x] != d)
	{
		y = Father[x];
		if (Father[y] == d)
		{
			Rotate(x);
			break;
		}
		if (GetDir(y) == GetDir(x)) Rotate(y);
		else Rotate(x);
		Rotate(x);
	}
	if (Father[x] == 0) Root = x;
}

int Find(int Num)
{
	int x = Root, k = Num;	
	while (Size[Son[x][0]] + 1 != k)
	{
		if (Size[Son[x][0]] + 1 > k) 
			x = Son[x][0];
		else
		{
			k -= Size[Son[x][0]] + 1;
			x = Son[x][1];
		}
	}
	return x;
}

bool Check(int p, int q, int len)
{
	if (len == 0) return true;
	int x, y;
	ULL Hp, Hq;
	x = Find(p); 
	y = Find(p + len + 1);
	Splay(x, 0);
	Splay(y, x);
	Hp = H[Son[y][0]];
	x = Find(q);
	y = Find(q + len + 1);
	Splay(x, 0);
	Splay(y, x);
	Hq = H[Son[y][0]];
	
	return Hp == Hq;
}

int main()
{
	Pow_Seed[0] = 1;
	for (int i = 1; i <= 100000; ++i) 
		Pow_Seed[i] = Pow_Seed[i - 1] * Seed;
	scanf("%s", Str + 1);
	l = strlen(Str + 1);
	n = l;
	Root = Build(0, l + 1);
	scanf("%d", &m);
	char f, ch;
	int Pos, x, y, p, q, l, r, mid, Ans;
	for (int i = 1; i <= m; ++i)
	{
		f = '-';
		while (f < 'A' || f > 'Z') f = getchar();
		switch (f)
		{
			case 'R' :
				scanf("%d %c", &Pos, &ch);
				x = Find(Pos + 1);
				Splay(x, 0);
				T[x] = ch;
				H[x] = (ULL)ch;
				Update(x);
				break;
			
			case 'I' :
				++n;
				scanf("%d %c", &Pos, &ch);
				x = Find(Pos + 1);
				y = Find(Pos + 2);
				Splay(x, 0);
				Splay(y, x);
				Son[y][0] = ++Index;
				T[Index] = ch;
				H[Index] = (ULL)ch;
				Size[Index] = 1;
				Father[Index] = y;
				Update(y); Update(x);
				break;
			
			case 'Q' : 
				scanf("%d%d", &p, &q);
				l = 0; r = n - gmax(p, q) + 1;
				while (l <= r)
				{
					mid = (l + r) >> 1;
					if (Check(p, q, mid)) 
					{
						Ans = mid;
						l = mid + 1;
					}
					else r = mid - 1;
				}
				printf("%d\n", Ans);
				break;
		}
	}
	return 0;
}

  

posted @ 2015-04-24 21:08  JoeFan  阅读(254)  评论(0编辑  收藏  举报