给定一个字符串,要求支持修改串中某个字符,在指定位置添加字符,以及求指定两个后缀的 LCP LCP 长度。

题解

提到LCP当然会想到后缀家族,不过我才疏学浅,并不知道后缀家族怎么支持修改和插入。

所以,Hash 大法好!

话说回来当时在长乐的时候写字符串哈希的暴力,可能是我姿势不太对?模数一直取不对。。这道题没有卡自然溢出,感觉还是很良心的。

好啦言归正传,对这个字符串建立一棵Splay,保证其中序遍历结果为原字符串。这样每一个Splay的节点都可以代表一个字串。如果要获得某个指定字串的哈希值,用Splay提区间就可以了。。

合并左右子树信息的时候,不知诸君喜欢以左为高位还是右为高位啊。。这里就不引战啦哈哈。

提示一个细节,这里为了Splay操作方便引入的左右端点节点会对某些节点的哈希值产生影响,但是由于Splay提取区间以后提取出来的区间对应的子树不会包括左右端点节点,而且这道题只需要用到提取区间,所以这样写没有错误。如果要让它的哈希值是正确的特判一下就好啦。

还有讲实话哈希那一套理论我真的是不怎么懂啊 T^T

代码

#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
const int MAX_N = 100000;
const int MAX_M = 150000;
const unsigned long long BASE = 233;
unsigned long long base[MAX_N + 1];
struct Splay {
struct Node {
Node *fa, *c[2], **root;
int size;
char val;
unsigned long long hash;
Node(Node *fa, Node **root, char val) : fa(fa), root(root), size(1), val(val), hash(val) {
c[0] = c[1] = NULL;
}
void maintain() {
size = (c[0] ? c[0]->size : 0) + (c[1] ? c[1]->size : 0) + 1;
hash = val;
if (c[1]) hash += c[1]->hash * BASE;
if (c[0]) hash = hash * base[c[0]->size] + c[0]->hash;
}
int lsize() {
return c[0] ? c[0]->size : 0;
}
int relation() {
return this == fa->c[1];
}
void rotate() {
Node *old = fa;
int x = relation();
fa = old->fa;
if (old->fa) old->fa->c[old->relation()] = this;
old->c[x] = c[x ^ 1];
if (c[x ^ 1]) c[x ^ 1]->fa = old;
c[x ^ 1] = old;
old->fa = this;
old->maintain(), maintain();
if (!fa) *root = this;
}
Node *splay(Node *target = NULL) {
while (fa != target) {
if (fa->fa == target) rotate();
else if (fa->relation() == relation()) fa->rotate(), rotate();
else rotate(), rotate();
}
return this;
}
} *root;
Splay() : root(NULL) {}
Node *build(const char *begin, const char *end, Node *par) {
if (begin > end) return NULL;
else if (begin == end) return new Node(par, &root, *begin);
else {
const char *mid = begin + (end - begin) / 2;
Node *v = new Node(par, &root, *mid);
v->c[0] = build(begin, mid - 1, v);
v->c[1] = build(mid + 1, end, v);
v->maintain();
return v;
}
}
void buildBound(int x) {
Node *v = root;
while (v->c[x]) v = v->c[x];
v->c[x] = new Node(v, &root, 0);
Node *u = v;
do {
u->maintain();
u = u->fa;
} while (u);
v->c[x]->splay();
}
void build(const char *begin, const char *end) {
root = build(begin, end, NULL);
buildBound(0);
buildBound(1);
}
Node *select(int k) {
int x = k + 1;
Node *v = root;
while (x != v->lsize() + 1) {
if (x <= v->lsize()) v = v->c[0];
else x -= v->lsize() + 1, v = v->c[1];
}
return v->splay();
}
Node *select(int l, int r) {
Node *u = select(l - 1), *v = select(r + 1);
u->splay();
v->splay(u);
return v->c[0];
}
Node *insert(int pos, char val) {
Node *u = select(pos), *v = select(pos + 1);
u->splay();
v->splay(u);
v->c[0] = new Node(v, &root, val);
Node *q = v->c[0];
do {
q->maintain();
q = q->fa;
} while (q);
return v->c[0]->splay();
}
void modify(int pos, char val) {
Node *v = select(pos);
v->val = val;
v->maintain();
}
unsigned long long query(int l, int r) {
return select(l, r)->hash;
}
int size() {
return root->size - 2;
}
} splay;
inline int lcp(int a, int b) {
int l = 0, r = std::min(splay.size() - a + 1, splay.size() - b + 1);
while (l != r) {
int mid = l + (r - l) / 2 + 1;
if (splay.query(a, a + mid - 1) == splay.query(b, b + mid - 1))
l = mid;
else
r = mid - 1;
}
return l;
}
char s[MAX_N + 1];
int main() {
base[0] = 1;
for (int i = 1; i <= MAX_N; i++) base[i] = base[i - 1] * BASE;
scanf("%s", s);
int n = strlen(s);
splay.build(s, s + n - 1);
int m;
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
char cmd[2];
scanf("%s", cmd);
if (cmd[0] == 'Q') {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", lcp(a, b));
} else if (cmd[0] == 'R') {
int pos;
char val[2];
scanf("%d %s", &pos, val);
splay.modify(pos, val[0]);
} else if (cmd[0] == 'I') {
int pos;
char val[2];
scanf("%d %s", &pos, val);
splay.insert(pos, val[0]);
}
}
return 0;
}