首页 > 技术文章 > 后缀平衡树

owenyu 2017-04-17 20:14 原文

如果需要动态维护后缀数组,支持在字符串前端插入一个字符,询问后缀的大小关系,如何做呢?

这是一个不断插入的问题,可以从增量的角度考虑。我们在前端插入一个字符,其实就是插入了一个新的后缀。我们的问题其实就是这个后缀排名多少。我们可以用平衡树维护一下后缀数组,从根节点开始二分比较这个后缀的大小,看看它应该被插到哪里。现在问题就变成了快速比较一个新的后缀和一个已有的后缀。

如果这个新的后缀和当前比较的后缀的首字符不同,那么比较结果是显然的;如果新的后缀和当前比较的后缀的首字符相同,那么问题就转化成了比较原来已有的两个后缀的大小关系。我们在平衡树的每个节点上维护一个值\(x\in [0,1]\),代表它的大小,左儿子为的关键值为\([l,mid]\),右儿子的关键值为\([mid,r]\),那么只要直接比较这个值就可以啦。

然而如果平衡树的深度过大,那么这个值会爆实数的精度。所以我们采用深度为\(O(logn)\)的平衡树。但如果平衡树需要旋转,那么它的子树需要全部重新计算关键值。所以我们需要使用重量平衡树,其子树大小均摊\(O(logn)\),所以每次插入旋转后整个子树重算一下。

代码:

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
	int x=0,f=1;
	char c=getchar();
	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*f;
}
const int maxn=3e5+1;
const int P=1e9+7;
char start[maxn];
int ans=0,Len,type,ansh=0;
int convert(int x) {
	if (!type) return x; else
	return (x^ans)%Len+1;
}
struct node {
	int hp,ch[2],fa;
	int thef,nexp;
	double lx,rx,x;
	void val(double _x,double _y) {
		lx=_x,rx=_y;
		x=(lx+rx)/2;
	}
};
struct SAT {
	node t[maxn];
	int tot,s[maxn],n,root,where[maxn];
	SAT () {
		n=0;
		root=tot=1;
		t[root].hp=-1;
		t[root].val(0,1);
	}
	int newnode() {
		t[++tot].hp=rand();
		return tot;
	}
	void revalue(int x,double lx,double rx) {
		if (!x) return;
		t[x].val(lx,rx);
		revalue(t[x].ch[0],t[x].lx,t[x].x);
		revalue(t[x].ch[1],t[x].x,t[x].rx);
	}
	bool rson(int x) {
		return t[t[x].fa].ch[1]==x;
	}
	void getval(int x) {
		int f=t[x].fa;
		if (rson(x)) t[x].val(t[f].x,t[f].rx); else t[x].val(t[f].lx,t[f].x);
	}
	void rotate(int x) {
		int f=t[x].fa,d=rson(x),c=t[x].ch[d^1];
		if (t[f].fa) t[t[f].fa].ch[rson(f)]=x;
		if (c) t[c].fa=f;
		t[x].fa=t[f].fa,t[f].fa=x;
		t[x].ch[d^1]=f,t[f].ch[d]=c;
		getval(x);
		getval(f);
	}
	bool compare(int x,int y) {
		if (t[x].thef!=t[y].thef) return t[x].thef<t[y].thef;
		return t[t[x].nexp].x<t[t[y].nexp].x;
	}
	void insert(int c) {
		s[++n]=c;
		int now=root,lsize=0;
		int nw=newnode();
		where[n]=nw;
		t[nw].thef=c;
		t[nw].nexp=tot-1;
		while (true) {
			bool cmp=compare(now,nw);
			int &tmp=t[now].ch[cmp];
			if (tmp) now=tmp; else {
				t[tmp=nw].fa=now;
				if (cmp) t[nw].val(t[now].x,t[now].rx); else t[nw].val(t[now].lx,t[now].x);
				break;
			}
		}
		while (t[t[nw].fa].hp>t[nw].hp) rotate(nw);
		revalue(nw,t[nw].lx,t[nw].rx);
	}
	int query(int x,int y) {
		int fx=where[Len-x+1],fy=where[Len-y+1];
		return t[fx].x<t[fy].x;
	}
} Sat;
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	srand(time(0));
	scanf("%s",start+1);
	Len=strlen(start+1);
	for (int i=Len;i>0;--i) Sat.insert(start[i]-'a'+1);
	int m=read();
	type=read();
	while (m--) {
		char op[3];
		scanf("%s",op);
		if (op[0]=='I') {
			char c[3];
			scanf("%s",c);
			++Len;
			Sat.insert(c[0]-'a'+1);
		} else if (op[0]=='Q') {
			int x=read(),y=read();
			x=convert(x),y=convert(y);
			ans=Sat.query(Len-x+1,Len-y+1)?x:y;
			ansh=((giant)ansh*23ll+(giant)ans)%P;
		}
	}
	printf("%d\n",ansh);
	return 0;
}

推荐阅读