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

时间:2023-03-09 16:07:37
[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;
}