zoukankan      html  css  js  c++  java
  • 后缀数组(无讲解)

    BZO2754: [SCOI2012]喵星球上的点名

    题目链接

    分析:

    • 把姓和名中间用一个分隔符分开,和询问串一起建立后缀数组。
    • 后缀数组上每个位置存对应串的标号。对于一个询问串(T),找到他对应的位置。
    • 考虑和他的lcp>=len(T)的位置都是合法的。左右二分/倍增提取出这样的区间。
    • 那么第一问转化成了区间颜色数量。树状数组即可。
    • 第二问相当于有若干次区间 只在第一次出现的位置 加。然后查询每个位置的和。方法类似,不过这次我们扫一遍序列。
    • 假设当前位置为i。先加入左端点是i的询问,在i处+1。然后处理这个点的信息。ans[id[i]] += [pre,i]的区间和(pre是上一次和i相等的位置)然后处理右端点等于i的询问,在该询问的左端点处-1。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define rep(n) for(i=1;i<=n;i++)
    #define per(n) for(i=n;i;i--)
    #define N 500050
    int w[N],sa[N],ht[N],rk[N],f[20][N],n,m,ln;
    int bg[N],ed[N],ws[N],wa[N],wb[N],Lg[N],id[N];
    int ql[N],qr[N],ans1[N],now[N],nxt[N],c[N],pid[N];
    int ans2[N],ke[N];
    void fix(int x,int v) {
    	for(;x<=ln;x+=x&(-x)) c[x]+=v;
    }
    int inq(int x) {
    	int re=0;
    	for(;x;x-=x&(-x)) re+=c[x]; return re;
    }
    struct A {
    	int l,r,id;
    	bool operator < (const A &x) const {
    		return l == x.l ? r < x.r : l < x.l;
    	}
    }q[N];
    priority_queue<pair<int,int> >pq;
    void build_sa(int n,int m) {
    	int i,j,p,*x=wa,*y=wb;
    	rep(m) ws[i]=0;
    	rep(n) ws[x[i]=w[i]]++;
    	rep(m) ws[i]+=ws[i-1];
    	per(n) sa[ws[x[i]]--]=i;
    	for(j=1;j<n;j<<=1) {
    		p=0;
    		for(i=n;i>n-j;i--) y[++p]=i;
    		rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
    		rep(m) ws[i]=0;
    		rep(n) ws[x[i]]++;
    		rep(m) ws[i]+=ws[i-1];
    		per(n) sa[ws[x[y[i]]]--]=y[i];
    		swap(x,y);
    		m=1;
    		rep(n) {
    			x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
    		}
    		if(m>n) break;
    	}
    	rep(n) rk[sa[i]]=i;
    	p=0;
    	rep(n) if(rk[i]!=n) {
    		j=rk[i]+1;
    		for(;w[i+p]==w[sa[j]+p];p++) ;
    		ht[j]=p;
    		if(p) p--;
    	}
    	Lg[0]=-1;
    	rep(n) Lg[i]=Lg[i>>1]+1,f[0][i]=ht[i];
    	for(i=1;(1<<i)<=n;i++) {
    		for(j=1;j<=n-(1<<i)+1;j++) {
    			f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    int gm(int l,int r) {
    	int len=Lg[r-l+1];return min(f[len][l],f[len][r-(1<<len)+1]);
    }
    int lcp(int x,int y) {
    	x=rk[x], y=rk[y];
    	if(x>y) swap(x,y);
    	if(x==y) return ln-sa[x]+1;
    	return gm(x+1,y);
    }
    void prt(int *a) {
    	int i;
    	for(i=1;i<=ln;i++) printf("%d ",a[i]); puts("");
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	int i,x,y,j;
    	int tmp=10000;
    	for(i=1;i<=n;i++) {
    		scanf("%d",&x);
    		bg[i]=ln+1;
    		while(x--) {
    			scanf("%d",&y); y++;
    			w[++ln]=y;
    		}
    		w[++ln]=++tmp;
    		
    		scanf("%d",&x);
    		while(x--) {
    			scanf("%d",&y); y++;
    			w[++ln]=y;
    		}
    		ed[i]=ln;
    		w[++ln]=++tmp;
    	}
    	for(i=1;i<=m;i++) {
    		scanf("%d",&x);
    		bg[i+n]=ln+1;
    		while(x--) {
    			scanf("%d",&y); y++;
    			w[++ln]=y;
    		}
    		ed[i+n]=ln;
    		w[++ln]=++tmp;
    	}
    	build_sa(ln,tmp);
    	for(i=1;i<=n;i++) for(j=bg[i];j<=ed[i];j++) id[j]=i;
    	for(i=1;i<=ln;i++) pid[i]=id[sa[i]];
    	// for(i=1;i<=m;i++) for(j=bg[i+n];j<=ed[i+n];j++) id[j]=i+n;
    	//prt(id);
    	//prt(w);
    	for(i=1;i<=m;i++) {
    		x=rk[bg[i+n]];
    		int len=ed[i+n]-bg[i+n]+1;
    		//printf("x=%d len=%d
    ",x,len);
    		int l=1,r=x;
    		while(l<r) {
    			int mid=(l+r)>>1;
    			if(x==mid||gm(mid+1,x)>=len) r=mid;
    			else l=mid+1;
    		}
    		ql[i]=l;
    		l=x,r=ln+1;
    		while(l<r) {
    			int mid=(l+r)>>1;
    			if(x==mid||gm(x+1,mid)>=len) l=mid+1;
    			else r=mid;
    		}
    		qr[i]=l-1;
    		q[i]=(A){ql[i],qr[i],i};
    
    		/*printf("Q%d
    ",i);
    		printf("ql=%d, qr=%d
    ",ql[i],qr[i]);
    		for(j=ql[i];j<=qr[i];j++) {
    			printf("%d
    ",id[sa[j]]);
    		}
    		puts("");*/
    	}
    	sort(q+1,q+m+1);
    	for(i=ln;i;i--) {
    		nxt[i]=now[pid[i]];
    		now[pid[i]]=i;
    	}
    	memset(now,0,sizeof(now));
    	for(i=1;i<=ln;i++) {
    		if(pid[i]&&!now[pid[i]]) {
    			now[pid[i]]=1;
    			fix(i,1);
    		}
    	}
    	j=1;
    	for(i=1;i<=m;i++) {
    		for(;j<q[i].l;j++) {
    			if(pid[j]) {
    				fix(j,-1);
    				if(nxt[j]) fix(nxt[j],1);
    			}
    		}
    		ans1[q[i].id]=inq(q[i].r);
    	}
    	for(i=1;i<=m;i++) printf("%d
    ",ans1[i]);
    	memset(now,0,sizeof(now));
    	memset(c,0,sizeof(c));
    	j=1;
    	for(i=1;i<=ln;i++) {
    		for(;q[j].l==i;j++) fix(i,1),pq.push(make_pair(-q[j].r,q[j].l));
    		if(pid[i]) {
    			ans2[pid[i]]+=inq(i)-inq(now[pid[i]]);
    			now[pid[i]]=i;
    		}
    		while(!pq.empty()&&pq.top().first==-i) {
    			fix(pq.top().second,-1); pq.pop();
    		}
    	}
    	for(i=1;i<=n;i++) printf("%d ",ans2[i]);
    }
    /*
    2 3 
    6 8 25 0 24 14 8 6 18 0 10 20 24 0 
    7 14 17 8 7 0 17 0 5 8 25 0 24 0 
    4 8 25 0 24 
    4 7 0 17 0 
    4 17 0 8 25 
    */
    
    

    BZOJ4556: [Tjoi2016&Heoi2016]字符串

    题目链接

    • 二分答案mid。然后像上一道题那样,根据[c,d]找一个合法区间ht>=mid。是否存在一个位置的a<=sa[i]<=b-mid+1。主席树上查即可。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    #include <map>
    #include <queue>
    #include <stack>
    #include <cctype>
    #include <vector>
    #include <set>
    #include <string>
    using namespace std;
    #define N 100050
    #define rep(n) for(i=1;i<=n;i++) 
    #define per(n) for(i=n;i;i--) 
    char w[N];
    int rr[N],rk[N],sa[N],ht[N],f[20][N],n,m;
    int ws[N],wa[N],wb[N],Lg[N],root[N];
    int siz[N*30],ls[N*30],rs[N*30],cnt;
    void update(int l,int r,int x,int &p,int q) {
    	p=++cnt; ls[p]=ls[q]; rs[p]=rs[q]; siz[p]=siz[q]+1;
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	if(x<=mid) update(l,mid,x,ls[p],ls[q]);
    	else update(mid+1,r,x,rs[p],rs[q]);
    }
    void build_sa(int n,int m) {
    	int i,j,p,*x=wa,*y=wb;
    	rep(m)ws[i]=0;
    	rep(n)ws[x[i]=rr[i]]++;
    	rep(m)ws[i]+=ws[i-1];
    	per(n)sa[ws[x[i]]--]=i;
    	for(j=1;j<n;j<<=1) {
    		p=0;
    		for(i=n;i>n-j;i--) y[++p]=i;
    		rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
    		rep(m) ws[i]=0;
    		rep(n) ws[x[i]]++;
    		rep(m) ws[i]+=ws[i-1];
    		per(n) sa[ws[x[y[i]]]--]=y[i];
    		swap(x,y); m=1;
    		rep(n) {
    			x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
    		}
    		if(m>n) break;
    	}
    	rep(n) rk[sa[i]]=i;
    	p=0;
    	rep(n) if(rk[i]!=n) {
    		j=rk[i]+1;
    		for(;w[i+p]==w[sa[j]+p];p++) ;
    		ht[j]=p; 
    		if(p) p--;
    	}
    	Lg[0]=-1;
    	rep(n) Lg[i]=Lg[i>>1]+1,f[0][i]=ht[i];
    	for(i=1;(1<<i)<=n;i++) {
    		for(j=1;j+(1<<i)-1<=n;j++) {
    			f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    int gm(int l,int r) {
    	int len=Lg[r-l+1];
    	return min(f[len][l],f[len][r-(1<<len)+1]);
    }
    int l1,l2,r1,r2,OK;
    void query(int l,int r,int x,int y,int p,int q) {
    	if(OK) return ;
    	if(x<=l&&y>=r) {
    		if(siz[q]-siz[p]>0) OK=1;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) query(l,mid,x,y,ls[p],ls[q]);
    	if(y>mid) query(mid+1,r,x,y,rs[p],rs[q]);
    }
    bool check(int mid) {
    	int x=rk[l2];
    	int d=0,p=x,ql,qr;
    	for(d=1;x-d>=1&&gm(x-d+1,x)>=mid;d<<=1) ;
    	for(d>>=1;d;d>>=1) {
    		if(p-d>=1&&gm(p-d+1,x)>=mid) p-=d;
    	}
    	ql=p;
    
    	d=0,p=x;
    	for(d=1;x+d<=n&&gm(x+1,x+d)>=mid;d<<=1) ;
    	for(d>>=1;d;d>>=1) {
    		if(p+d<=n&&gm(x+1,p+d)>=mid) p+=d;
    	}
    	qr=p;
    
    	OK=0;
    	query(1,n,l1,r1-mid+1,root[ql-1],root[qr]);
    	return OK;
    }
    int main() {
    	scanf("%d%d%s",&n,&m,w+1);
    	int i;
    	for(i=1;i<=n;i++) rr[i]=w[i]-'a'+1;
    	build_sa(n,255);
    	rep(n) {
    		update(1,n,sa[i],root[i],root[i-1]);
    	}
    	while(m--) {
    		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
    		int l=0,r=min(r1-l1,r2-l2)+2;
    		while(l<r) {
    			int mid=(l+r)>>1;
    			if(check(mid)) l=mid+1;
    			else r=mid;
    		}
    		printf("%d
    ",l-1);
    	}
    }
    

    BZOJ4199: [Noi2015]品酒大会

    题目链接

    分析:

    • 把sa[]按ht排序。并查集维护即可。
    • 维护连通块内方案数和最大值。由于有负数需要再维护一个最小值。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    #define N 300050
    typedef long long ll;
    #define rep(n) for(i=1;i<=n;i++) 
    #define per(n) for(i=n;i;i--)
    int rr[N],n,val[N],sa[N],rk[N],ht[N],ws[N],wa[N],wb[N];
    int t[N],siz[N],fa[N];
    ll ans1[N],ans2[N];
    int mn[N],mx[N];
    char w[N];
    int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
    void build_sa(int n,int m) {
    	int i,j,p,*x=wa,*y=wb;
    	rep(m) ws[i]=0;
    	rep(n) ws[x[i]=rr[i]]++;
    	rep(m) ws[i]+=ws[i-1];
    	per(n) sa[ws[x[i]]--]=i;
    	for(j=1;j<n;j<<=1) {
    		p=0;
    		for(i=n;i>n-j;i--) y[++p]=i;
    		rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
    		rep(m) ws[i]=0;
    		rep(n) ws[x[i]]++;
    		rep(m) ws[i]+=ws[i-1];
    		per(n) sa[ws[x[y[i]]]--]=y[i];
    		swap(x,y);
    		m=1;
    		rep(n) {
    			x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
    		}
    		if(m>n) break;
    	}
    	rep(n) rk[sa[i]]=i;
    	p=0;
    	rep(n) if(rk[i]!=n) {
    		j=rk[i]+1;
    		for(;w[i+p]==w[sa[j]+p];p++) ;
    		ht[j]=p;
    		if(p) p--;
    	}
    }
    inline bool cmp(const int &x,const int &y) {return ht[x]>ht[y];}
    int main() {
    	scanf("%d%s",&n,w+1);
    	int i;
    	rep(n) ans2[i]=-1ll<<60;
    	for(i=1;i<=n;i++) scanf("%d",&val[i]);
    	for(i=1;i<=n;i++) rr[i]=w[i]-'a'+1;
    	build_sa(n,255);
    	for(i=1;i<=n;i++) t[i]=i,fa[i]=i,mn[i]=mx[i]=val[sa[i]],siz[i]=1;
    	sort(t+1,t+n+1,cmp);
    	for(i=1;i<=n;i++) {
    		int x=t[i],y=x-1;
    		if(x==1) continue;
    		int dx=find(x),dy=find(y);
    		ans1[ht[x]]+=ll(siz[dx])*siz[dy];
    		ans2[ht[x]]=max(ans2[ht[x]],max(ll(mn[dx])*mn[dy],ll(mx[dx])*mx[dy]));
    		fa[dx]=dy; siz[dy]+=siz[dx]; mn[dy]=min(mn[dy],mn[dx]); mx[dy]=max(mx[dy],mx[dx]);
    	}
    	per(n) ans1[i-1]+=ans1[i],ans2[i-1]=max(ans2[i-1],ans2[i]);
    	rep(n) printf("%lld %lld
    ",ans1[i-1],ans1[i-1]?ans2[i-1]:0);
    }
    

    BZOJ3230: 相似子串

    分析:

    • 考虑sa数组每一位都对应一些本质不同字符串。这个肯定是按字典序来的。
    • 维护n-sa[i]+1-ht[i]的前缀和。每次询问二分即可。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    #define N 100050
    typedef long long ll;
    #define rep(n) for(i=1;i<=n;i++)
    #define per(n) for(i=n;i;i--)
    #define pi pair<int,int>
    int n,m;
    ll v[N],sv[N];
    char w[N];
    struct SA {
    	int rr[N],sa[N],ht[N],ws[N],wa[N],wb[N],rk[N],Lg[N],f[20][N];
    	void build_sa(int n,int m) {
    		int i,j,p,*x=wa,*y=wb;
    		rep(m) ws[i]=0;
    		rep(n) ws[x[i]=rr[i]]++;
    		rep(m) ws[i]+=ws[i-1];
    		per(n) sa[ws[x[i]]--]=i;
    		for(j=1;j<n;j<<=1) {
    			p=0;
    			for(i=n;i>n-j;i--) y[++p]=i;
    			rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
    			rep(m) ws[i]=0;
    			rep(n) ws[x[i]]++;
    			rep(m) ws[i]+=ws[i-1];
    			per(n) sa[ws[x[y[i]]]--]=y[i];
    			swap(x,y);
    			m=1;
    			rep(n) x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
    			if(m>n) break;
    		}
    		rep(n) rk[sa[i]]=i;
    		p=0;
    		rep(n) if(rk[i]!=n) {
    			j=rk[i]+1;
    			for(;rr[i+p]==rr[sa[j]+p];p++) ;
    			ht[j]=p;
    			if(p) p--;
    		}
    		Lg[0]=-1;
    		rep(n) Lg[i]=Lg[i>>1]+1;
    		rep(n) f[0][i]=ht[i];
    		for(i=1;(1<<i)<=n;i++) for(j=1;j+(1<<i)-1<=n;j++) f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    	}
    	int gm(int l,int r) {
    		int len=Lg[r-l+1];
    		return min(f[len][l],f[len][r-(1<<len)+1]);
    	}
    	pi getk(ll K) {
    		int l=1,r=n+1;
    		while(l<r) {
    			int mid=(l+r)>>1;
    			if(sv[mid]>=K) r=mid;
    			else l=mid+1;
    		}
    		return make_pair(sa[l],int(n-sv[l]+K));
    	}
    	int LCP(int x,int y) {
    		if(x==y) return n-x+1;
    		x=rk[x],y=rk[y];
    		if(x>y) swap(x,y);
    		return gm(x+1,y);
    	}
    	int lcp(int l1,int r1,int l2,int r2) {
    		int t=LCP(l1,l2);
    		//printf("%d %d %d %d
    ",l1,r1,l2,r2);
    		//printf("t=%d
    ",t);
    		return min(t,min(r1-l1+1,r2-l2+1));
    	}
    }s1,s2;
    ll SQ(ll x) {return x*x;}
    int main() {
    	scanf("%d%d%s",&n,&m,w+1);
    	int i;
    	rep(n) s1.rr[i]=w[i]-'a'+1;
    	rep(n) s2.rr[i]=s1.rr[n-i+1];
    	s1.build_sa(n,255);
    	s2.build_sa(n,255);
    	rep(n) {
    		v[i]=n-s1.sa[i]+1-s1.ht[i];
    		sv[i]=sv[i-1]+v[i];
    	}
    	while(m--) {
    		ll x,y;
    		scanf("%lld%lld",&x,&y);
    		if(x>sv[n]||y>sv[n]) {
    			puts("-1"); continue;
    		}
    		pi tx=s1.getk(x);
    		pi ty=s1.getk(y);
    		int l1=tx.first,r1=tx.second;
    		int l2=ty.first,r2=ty.second;
    		//printf("%d %d %d %d
    ",l1,r1,l2,r2);
    		printf("%lld
    ",SQ(s1.lcp(l1,r1,l2,r2))+SQ(s2.lcp(n-r1+1,n-l1+1,n-r2+1,n-l2+1)));
    	}
    }
    
  • 相关阅读:
    「考试」省选27
    「考试」省选26
    「考试」省选25
    $dy$讲课总结
    「笔记」$Min\_25$筛
    「考试」省选24
    「总结」多项式生成函数例题(4)
    「总结」多项式生成函数相关(4)
    「考试」省选23
    「总结」后缀3
  • 原文地址:https://www.cnblogs.com/suika/p/10014920.html
Copyright © 2011-2022 走看看