HDU6704 K-th occurrence(后缀数组+二分+主席树)
根据题意,寻找子串出现的第k次的开头。寻找第k大,一般可以想到用主席树来维护。
但是这题还需要更多的转化,首先想到我们如果想知道子串匹配,一个可以考虑kmp,但是因为询问过多,不太科学。
一般还有两种,一种是哈希算法,一种是后缀数组求lcp。考虑哈希算法,感觉可做性不是很大,因为他要多次匹配。考虑后缀数组
我们发现一个重要的信息,也就是后缀数组的height数组是随着位置的远离而变小的,因此每次的询问其实都有一段固定的区间。这个区间可以通过两次二分求取。
这样我们的任务就变成了在后缀数组的排名序列上,二分出一段满足条件的区间,我们在这上面求取第k大值,后者可以用主席树维护。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; using namespace std; typedef long long ll; const int N=1e5+10; int n,q; char t[N]; int sa[N],rk[N],od[N],cnt[N],id[N],px[N]; ll h[N]; int root[N]; ll f[N][26]; ll lg[N],idx; struct node{ int l,r; int cnt; }tr[40*N]; bool cmp(int x, int y, int w){ return od[x]==od[y]&&od[x+w]==od[y+w]; } void build(int &rt,int l,int r){ rt=++idx; tr[rt]={l,r,0}; if(l==r) return; int mid=l+r>>1; build(tr[rt].l,l,mid); build(tr[rt].r,mid+1,r); } void da(int n,int m){ int i; memset(cnt,0,sizeof cnt); for(i=1;i<=n;i++) cnt[rk[i]=t[i]]++; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i; int p; for(int w=1;w<n;w<<=1,m=p){ p=0; for(i=n;i>n-w;i--) id[++p]=i; for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w; memset(cnt,0,sizeof cnt); for(i=1;i<=n;i++) cnt[px[i]=rk[id[i]]]++; for(i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i]; for(i=0;i<=n;i++) od[i]=rk[i]; for(p=0,i=1;i<=n;i++){ rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p; } } } void st(int n){ int i,j; for(i=1;i<=n;i++) f[i][0]=h[i]; lg[1]=0; for(i=2;i<=n;i++) lg[i]=lg[i>>1]+1; for(j=1;j<=lg[n];j++){ for(i=1;i+(1<<j)-1<=n;i++){ f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]); } } } int query(int l,int r){ if(l>r) swap(l,r); l++; int len=lg[r-l+1]; return min(f[l][len],f[r-(1<<len)+1][len]); } void get_height(int n){ int i; for(i=1;i<=n;i++){ if(rk[i]==1) continue; int a=max(h[rk[i-1]]-1,1ll*0); while(i+a<=n&&t[i+a]==t[sa[rk[i]-1]+a]) a++; h[rk[i]]=a; } st(n); } int getL(int l,int r,int len,int x){ while(l<r){ int mid=l+r>>1; if(query(mid,x)>=len){ r=mid; } else{ l=mid+1; } } return l; } int getR(int l,int r,int len,int x){ while(l<r){ int mid=l+r+1>>1; if(query(mid,x)>=len) l=mid; else r=mid-1; } return l; } int getans(int p,int q,int l,int r,int k){ if(l==r) return l; int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt; int mid=l+r>>1; if(k<=cnt) return getans(tr[p].l,tr[q].l,l,mid,k); else return getans(tr[p].r,tr[q].r,mid+1,r,k-cnt); } int solve(int l,int r,int k){ int len=r-l+1; int L=getL(1,rk[l],len,rk[l]); int R=getR(rk[l],n,len,rk[l]); if(k>R-L+1) return -1; return getans(root[L-1],root[R],1,n,k); } void add(int &rt,int l,int r,int p,int pos,int k){ rt=++idx;tr[rt]=tr[p]; if(l==r){ tr[rt].cnt+=k; return ; } int mid=l+r>>1; if(pos<=mid) add(tr[rt].l,l,mid,tr[p].l,pos,k); else add(tr[rt].r,mid+1,r,tr[p].r,pos,k); tr[rt].cnt=tr[tr[rt].l].cnt+tr[tr[rt].r].cnt; } int main(){ //ios::sync_with_stdio(false); int T; cin>>T; while(T--){ scanf("%d%d",&n,&q); scanf("%s",t+1); int lent=strlen(t+1); da(lent,150); get_height(lent); idx=0; build(root[0],1,n); for(int i=1;i<=n;i++){ int pos=sa[i]; add(root[i],1,n,root[i-1],pos,1); } while(q--){ int l,r,k; scanf("%d%d%d",&l,&r,&k); cout<<solve(l,r,k)<<endl; } } }
没有人不辛苦,只有人不喊疼