zoukankan      html  css  js  c++  java
  • 后缀平衡树

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

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

    如果这个新的后缀和当前比较的后缀的首字符不同,那么比较结果是显然的;如果新的后缀和当前比较的后缀的首字符相同,那么问题就转化成了比较原来已有的两个后缀的大小关系。我们在平衡树的每个节点上维护一个值(xin [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
    ",ansh);
    	return 0;
    }
    
  • 相关阅读:
    博客园样式设置
    最坏情况为线性时间的选择算法
    棋盘覆盖
    矩阵乘法的Strassen算法及时间复杂度
    大整数乘法及算法时间复杂度
    全排列问题的递归算法(Perm)
    python的lambda
    python的zip函数
    python操作队列
    mysql基础命令
  • 原文地址:https://www.cnblogs.com/owenyu/p/6724597.html
Copyright © 2011-2022 走看看