HDU6704 K-th occurrence(字尾陣列+二分+主席樹)
阿新 • • 發佈:2020-09-15
根據題意,尋找子串出現的第k次的開頭。尋找第k大,一般可以想到用主席樹來維護。
但是這題還需要更多的轉化,首先想到我們如果想知道子串匹配,一個可以考慮kmp,但是因為詢問過多,不太科學。
一般還有兩種,一種是雜湊演算法,一種是字尾陣列求lcp。考慮雜湊演算法,感覺可做性不是很大,因為他要多次匹配。考慮字尾陣列
我們發現一個重要的資訊,也就是字尾陣列的height陣列是隨著位置的遠離而變小的,因此每次的詢問其實都有一段固定的區間。這個區間可以通過兩次二分求取。
這樣我們的任務就變成了在後綴陣列的排名序列上,二分出一段滿足條件的區間,我們在這上面求取第k大值,後者可以用主席樹維護。
#include<bits/stdc++.h> usingView Codenamespace 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; } } }