K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)
大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置.
这是一篇很好的题解:
https://blog.csdn.net/sdauguanweihong/article/details/100063096
加点个人:
我对上面的题解更为详细的解释下:
后缀数组处理出来的heigth[] 数组 有个这样的性质:
对于排名 a 的后缀字符串 与排名 b 的后缀字符串 ,他们的最长公共前缀的长度为 min{heigth[a+1],heigth[a+2],heigth[b]};
依据这样的性质就可以二分线段树出[l,r] 这个字符串 是在多少排名的区间[L,R]了;
注意:我本想贪图方便用先二分l然后判断[l,pos] 的最小值复杂度为log*log 这个会T的
所以找这个区间要用log的方法,就是在存了最小值的线段树里面去搜左孩子啊右孩子什么的
#include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f const int maxn = 1000050; char s[maxn]; int y[maxn],x[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn],wt[30]; int n,k,q; int get_SA(int m){ for(int i=0 ; i<=m ; i++) c[i]=0; for(int i=0 ; i<=n ; i++) sa[i]=0; for(int i=1 ; i<=n ; i++) ++c[x[i]=s[i]]; for(int i=2 ; i<=m ; i++) c[i]+=c[i-1]; for(int i=n ; i>=1 ; i--) sa[c[x[i]]--]=i; for(int k=1 ; k<=n ; k<<=1){ int num=0; for(int i=n-k+1 ; i<=n ; i++) y[++num]=i; for(int i=1 ; i<=n ; i++) if(sa[i]>k) y[++num]=sa[i]-k; for(int i=1 ; i<=m ; i++) c[i]=0; for(int i=1 ; i<=n ; i++) ++c[x[i]]; for(int i=2 ; i<=m ; i++) c[i]+=c[i-1]; for(int i=n ; i>=1 ; i--) sa[c[x[y[i]]]--]=y[i],y[i]=0; swap(x,y); x[sa[1]]=1; num=1; for(int i=2 ; i<=n ; i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num; if (num==n) break; m=num; } } int get_height() { int k=0; for (int i=1; i<=n; ++i) rk[sa[i]]=i; for (int i=1; i<=n; ++i) { if (rk[i]==1) continue; if (k) --k; int j=sa[rk[i]-1]; while (j+k<=n && i+k<=n && s[i+k]==s[j+k]) ++k; height[rk[i]]=k; } } int mi[maxn<<2]; void pushup(int rt){ mi[rt]=min(mi[rt<<1],mi[rt<<1|1]); } void build(int l,int r,int rt) { if ( l==r ) { mi[rt]=height[l]; return ; } int m = (l+r) >> 1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); pushup(rt); } int solvel(int o , int l , int r , int x , int v){ int mid=(l+r)>>1; if(r<=x){ if(l==r) return mi[o]>=v?l:-1; if(mi[o<<1|1]<v) return solvel(o<<1|1,mid+1,r,x,v); int t=solvel(o<<1,l,mid,x,v); return t==-1?mid+1:t; } if(mid>=x) return solvel(o<<1,l,mid,x,v); int R=solvel(o<<1|1,mid+1,r,x,v); if (R==-1||R>mid+1) return R; int L = solvel(o<<1,l,mid,x,v); return L==-1?R:L; } int solver(int o, int l, int r, int x, int v){ int mid=(l+r)>>1; if (x<=l) { if (l==r) return mi[o]>=v?l:-1; if (mi[o<<1]<v) return solver(o<<1,l,mid,x,v); int t = solver(o<<1|1,mid+1,r,x,v); return t==-1?mid:t; } if (mid<x) return solver(o<<1|1,mid+1,r,x,v); int L = solver(o<<1,l,mid,x,v); if (L==-1||L<mid) return L; int R = solver(o<<1|1,mid+1,r,x,v); return R==-1?L:R; } int tot; int lson[maxn<<5],rson[maxn<<5],T[maxn],cc[maxn<<5]; void zhu_build(int &root,int l,int r) { root=++tot; if ( l==r ) return; int mid=(l+r)/2; zhu_build(lson[root],l,mid); zhu_build(rson[root],mid+1,r); } void update(int root,int &rt,int p,int val,int l,int r) { rt=++tot; lson[rt]=lson[root],rson[rt]=rson[root]; cc[rt]=cc[root]+val; if ( l==r ) return; int mid=(l+r)/2; if ( p<=mid ) update(lson[rt],lson[rt],p,val,l,mid); else update(rson[rt],rson[rt],p,val,mid+1,r); } int query(int rt_,int rt,int l,int r,int k) { if ( l==r ) return l; int mid=(l+r)/2; int sum=cc[lson[rt_]]-cc[lson[rt]]; if ( sum>=k ) return query(lson[rt_],lson[rt],l,mid,k); else return query(rson[rt_],rson[rt],mid+1,r,k-sum); } int main(){ int _;scanf("%d",&_); while(_--){ scanf("%d%d%s",&n,&q,s+1); get_SA(122); get_height(); build(1,n,1); tot=0; zhu_build(T[0],1,n); for(int i=1 ; i<=n ; i++){ update(T[i-1],T[i],sa[i],1,1,n); } while(q--){ int l,r;scanf("%d%d%d",&l,&r,&k); int p=rk[l]; int ql = p>1?solvel(1,1,n,p,r-l+1)-1:1; int qr = p<n?solver(1,1,n,p+1,r-l+1):n; if (ql<0) ql = p; if (qr<0) qr = p; int ans; if(qr-ql+1<k) ans=-1; else ans=query(T[qr],T[ql-1],1,n,k); printf("%d\n",ans); } } }