[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; }