题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点。
解法一:
求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[N],buf1[N],buf2[N],c[N],n,rnk[N],ht[N],ST[N][20],Log[N],m; 7 void Sort(int* x,int* y,int m) { 8 for(int i=0; i<m; ++i)c[i]=0; 9 for(int i=0; i<n; ++i)++c[x[i]]; 10 for(int i=1; i<m; ++i)c[i]+=c[i-1]; 11 for(int i=n-1; i>=0; --i)sa[--c[x[y[i]]]]=y[i]; 12 } 13 void da(int* s,int n,int m=1000) { 14 int *x=buf1,*y=buf2; 15 x[n]=y[n]=-1; 16 for(int i=0; i<n; ++i)x[i]=s[i],y[i]=i; 17 Sort(x,y,m); 18 for(int k=1; k<n; k<<=1) { 19 int p=0; 20 for(int i=n-k; i<n; ++i)y[p++]=i; 21 for(int i=0; i<n; ++i)if(sa[i]>=k)y[p++]=sa[i]-k; 22 Sort(x,y,m),p=1,y[sa[0]]=0; 23 for(int i=1; i<n; ++i)y[sa[i]]=x[sa[i-1]]==x[sa[i]]&&x[sa[i-1]+k]==x[sa[i]+k]?p-1:p++; 24 if(p==n)break; 25 swap(x,y),m=p; 26 } 27 } 28 void getht() { 29 for(int i=0; i<n; ++i)rnk[sa[i]]=i; 30 ht[0]=0; 31 for(int i=0,k=0; i<n; ++i) { 32 if(k)--k; 33 if(!rnk[i])continue; 34 for(; s[i+k]==s[sa[rnk[i]-1]+k]; ++k); 35 ht[rnk[i]]=k; 36 } 37 } 38 void initST() { 39 for(int i=1; i<n; ++i)ST[i][0]=ht[i]; 40 for(int j=1; (1<<j)<=n; ++j) 41 for(int i=1; i+(1<<j)-1<n; ++i) 42 ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]); 43 } 44 int lcp(int l,int r) { 45 if(l==r)return n-sa[l]; 46 if(l>r)swap(l,r); 47 l++; 48 int k=Log[r-l+1]; 49 return min(ST[l][k],ST[r-(1<<k)+1][k]); 50 } 51 int rt[N],ls[N*30],rs[N*30],sum[N*30],tot; 52 #define mid ((l+r)>>1) 53 int cpy(int v) {int u=++tot; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;} 54 void upd(int& u,int v,int p,int l=0,int r=n-1) { 55 u=cpy(v); 56 sum[u]++; 57 if(l==r)return; 58 p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+1,r); 59 } 60 int qry(int u,int v,int k,int l=0,int r=n-1) { 61 if(l==r)return l; 62 int cnt=sum[ls[u]]-sum[ls[v]]; 63 return k<=cnt?qry(ls[u],ls[v],k,l,mid):qry(rs[u],rs[v],k-cnt,mid+1,r); 64 } 65 void build() { 66 da(s,n),getht(),initST(); 67 tot=0; 68 for(int i=1; i<=n; ++i)upd(rt[i],rt[i-1],sa[i-1]); 69 } 70 int main() { 71 Log[0]=-1; 72 for(int i=1; i<N; ++i)Log[i]=Log[i>>1]+1; 73 int T; 74 for(scanf("%d",&T); T--;) { 75 scanf("%d%d%s",&n,&m,buf); 76 for(int i=0; i<n; ++i)s[i]=buf[i]; 77 s[n]=0; 78 build(); 79 while(m--) { 80 int l,r,k; 81 scanf("%d%d%d",&l,&r,&k); 82 l--,r--; 83 int L,R,M=rnk[l],len=r-l+1; 84 L=R=M; 85 for(int i=Log[n]; i>=0; --i)if(L-(1<<i)>=0&&lcp(M,L-(1<<i))>=len)L-=(1<<i); 86 for(int i=Log[n]; i>=0; --i)if(R+(1<<i)<n&&lcp(M,R+(1<<i))>=len)R+=(1<<i); 87 printf("%d ",k<=sum[rt[R+1]]-sum[rt[L]]?qry(rt[R+1],rt[L],k)+1:-1); 88 } 89 } 90 return 0; 91 }
解法二:
建立后缀自动机,对后缀树(fail树)作线段树合并可得到每个结点包含的全部right值。对每个询问倍增找到待查询子串所对应的结点,然后线段树上查询第k大即可。
可持久化合并可以实现在线查询。
fail树上dfs序建可持久化线段树貌似也可以(这句话怎么这么耳熟?)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=2e5+10,M=26; 5 char s[N]; 6 int n,m,fa[N],go[N][M],mxl[N],last,tot,samrt[N],c[N],ss[N],Fa[N][20]; 7 int rt[N],ls[N*50],rs[N*50],sum[N*50],tot2; 8 #define mid ((l+r)>>1) 9 int cpy(int v) {int u=++tot2; ls[u]=ls[v],rs[u]=rs[v],sum[u]=sum[v]; return u;} 10 void upd(int& u,int v,int p,int l=1,int r=n) { 11 u=cpy(v),sum[u]++; 12 if(l==r)return; 13 p<=mid?upd(ls[u],ls[v],p,l,mid):upd(rs[u],rs[v],p,mid+1,r); 14 } 15 void mg(int& w,int u,int v) { 16 if(!u||!v) {w=u|v; return;} 17 w=cpy(u),sum[w]+=sum[v]; 18 mg(ls[w],ls[u],ls[v]),mg(rs[w],rs[u],rs[v]); 19 } 20 int qry(int u,int k,int l=1,int r=n) { 21 if(l==r)return l; 22 return k<=sum[ls[u]]?qry(ls[u],k,l,mid):qry(rs[u],k-sum[ls[u]],mid+1,r); 23 } 24 int newnode(int l) {int u=++tot; rt[u]=0,mxl[u]=l,memset(go[u],0,sizeof go[u]); return u;} 25 void add(int ch,int r) { 26 int p=last,np=last=newnode(mxl[p]+1); 27 samrt[r]=np; 28 upd(rt[np],rt[np],r,1); 29 for(; p&&!go[p][ch]; p=fa[p])go[p][ch]=np; 30 if(!p)fa[np]=1; 31 else { 32 int q=go[p][ch]; 33 if(mxl[q]==mxl[p]+1)fa[np]=q; 34 else { 35 int nq=newnode(mxl[p]+1); 36 memcpy(go[nq],go[q],sizeof go[q]); 37 fa[nq]=fa[q],fa[q]=fa[np]=nq; 38 for(; p&&go[p][ch]==q; p=fa[p])go[p][ch]=nq; 39 } 40 } 41 } 42 void build() { 43 tot=tot2=0,last=newnode(0); 44 for(int i=0; i<n; ++i)add(s[i]-'a',i+1); 45 for(int i=0; i<=tot; ++i)c[i]=0; 46 for(int i=1; i<=tot; ++i)++c[mxl[i]]; 47 for(int i=1; i<=tot; ++i)c[i]+=c[i-1]; 48 for(int i=1; i<=tot; ++i)ss[--c[mxl[i]]]=i; 49 for(int i=tot-1; i>0; --i)mg(rt[fa[ss[i]]],rt[fa[ss[i]]],rt[ss[i]]); 50 for(int i=1; i<=tot; ++i)Fa[i][0]=fa[i]; 51 for(int k=1; k<20; ++k)for(int i=1; i<=tot; ++i)Fa[i][k]=Fa[Fa[i][k-1]][k-1]; 52 } 53 int main() { 54 int T; 55 for(scanf("%d",&T); T--;) { 56 scanf("%d%d%s",&n,&m,s); 57 build(); 58 while(m--) { 59 int l,r,k; 60 scanf("%d%d%d",&l,&r,&k); 61 int u=samrt[r],len=r-l+1; 62 if(mxl[fa[u]]+1>len) { 63 for(int k=19; k>=0; --k)if(mxl[fa[Fa[u][k]]]+1>len)u=Fa[u][k]; 64 u=fa[u]; 65 } 66 printf("%d ",k<=sum[rt[u]]?qry(rt[u],k)-len+1:-1); 67 } 68 } 69 return 0; 70 }