Problem Description
You are given a string S consisting of only lowercase english letters and some queries.
For each query (l,r,k), please output the starting position of the k-th occurence of the substring SlSl+1...Sr in S.
Input
The first line contains an integer T(1≤T≤20), denoting the number of test cases.
The first line of each test case contains two integer N(1≤N≤105),Q(1≤Q≤105), denoting the length of S and the number of queries.
The second line of each test case contains a string S(|S|=N) consisting of only lowercase english letters.
Then Q lines follow, each line contains three integer l,r(1≤l≤r≤N) and k(1≤k≤N), denoting a query.
There are at most 5 testcases which N is greater than 103.
Output
For each query, output the starting position of the k-th occurence of the given substring.
If such position don't exists, output −1 instead.
Sample Input
2 12 6 aaabaabaaaab 3 3 4 2 3 2 7 8 3 3 4 2 1 4 2 8 12 1 1 1 a 1 1 1
Sample Output
5 2 -1 6 9 8 1
题目大意:给出一个字符串,q次询问,每次询问l, r, k.就是l~r这段区间的字串在文本串中出现第k次的位置。
解题思路:后缀数组求解。在height数组上找到一段大于所要查找的字符串长度的区间。所找到的这个区间在sa数组中对应区间做一个主席树查询区间第k大就可以了。比较麻烦的是在用RMQ找区间左右边界的情况需要多考虑一下。
/* @Author: Top_Spirit @Language: C++ */ #include<bits/stdc++.h> using namespace std; const int maxn=100010; int sa[maxn]; int t1[maxn],t2[maxn],c[maxn]; int Rank[maxn],height[maxn]; void getsa(int *s,int n,int m){ int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=s[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1){ p=0; for(i=n-j;i<n;i++)y[p++]=i; for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[y[i]]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; if(p>=n)break; m=p; } } void getHeight(int *s,int n){ int k = 0, j ; for (int i = 1 ;i <= n; i++) Rank[sa[i]] = i; for (int i = 0; i < n; height[Rank[i++]] = k){ for (k ? k-- : 0, j = sa[Rank[i] - 1]; s[i + k] == s[j + k]; k++); } } int mm[maxn],dp[maxn][20]; //dp[i][j]表示i至i+2^j-1的最值。求最大值初始为0,最小值初始为inf void initrmq(int n,int b[]){ mm[0]=-1; for(int i=1;i<=n;i++){ mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; dp[i][0] = b[i]; } for(int j =1; j <= mm[n];j++) for(int i = 1;i + (1<<j) -1 <=n;i++){ dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); } } int rmq(int x,int y){ int k = mm[y-x+1]; return min(dp[x][k],dp[y-(1<<k)+1][k]); } int cnt,root[maxn]; struct node{int l,r,sum;}T[maxn*40]; void update(int l,int r,int &x,int y,int pos){ T[++cnt]=T[y],T[cnt].sum++,x=cnt;//首先让新树结点=旧树,然后哪边不同(即哪边修改),创造哪边 if(l==r) return; int mid=l+r>>1; if(pos<=mid) update(l,mid,T[x].l,T[y].l,pos); else update(mid+1,r,T[x].r,T[y].r,pos); } int query(int l,int r,int x,int y,int k){ if(l==r) return l; int mid=l+r>>1; int sum=T[T[y].l].sum-T[T[x].l].sum;//y树的左区间和减去(x-1)树的左区间和为x~y区间的左区间和 if(sum>=k) return query(l,mid,T[x].l,T[y].l,k); return query(mid+1,r,T[x].r,T[y].r,k-sum); } char str[maxn]; int s[maxn]; int main(){ int kase, m, q ; scanf("%d", &kase) ; while(kase--){ cnt=0; scanf("%d%d", &m, &q) ; scanf("%s", str) ; int n = strlen(str); for(int i = 0; i <= n; i++) s[i] = str[i] ; getsa(s,n+1,128); getHeight(s,n); printf("sa :"); for(int i=1;i<=n;i++) printf("%d ",sa[i]); printf("\n"); printf("height:"); for(int i=1;i<=n;i++) printf("%d ",height[i]); printf("\n"); printf("rank :"); for(int i=0;i<=n;i++) printf("%d ",Rank[i]); printf("\n"); for(int i=0;i<=n*31;i++) T[i].l=T[i].sum=T[i].r=0; for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],sa[i]+1); initrmq(n,height); while(q--){ int ll,rr,k; scanf("%d%d%d",&ll,&rr,&k); int len=rr-ll+1; int id=Rank[ll-1]; int l=0,r=id-1,ansl=id,ansr=id;//先左 if(height[id]>=len){ while(l<=r){ int mid=l+r>>1; if(rmq(id-mid,id)>=len){ l=mid+1; ansl=id-mid; } else r=mid-1; } } else{ ansl=id+1; id++; if(ansl>n){ if(k==1) printf("%d\n",ll); else printf("-1\n"); continue; } } l=0,r=n-id; while(l<=r){ int mid=l+r>>1; if(rmq(id,id+mid)>=len){ l=mid+1; ansr=id+mid; } else r=mid-1; } printf("ansl=%d,ansr=%d\n",ansl,ansr); if(ansl==ansr&&height[ansl]<len){ if(k>1) printf("-1\n"); else printf("%d\n",sa[ansl]+1); continue; } if(ansr-ansl+2<k){ printf("-1\n"); continue; } int ans=query(1,n,root[ansl-2],root[ansr],k); printf("%d\n",ans); } } return 0; } /* 100 7 4 aaaaabb 1 2 4 1 2 3 1 2 2 1 2 1 12 6 aaabaabaaaab 3 3 4 2 3 2 7 8 3 3 4 2 1 4 2 8 12 1 1 1 a 1 1 1 11 5 abbbabcacaa 5 6 2 7 8 2 3 3 4 4 5 2 10 10 3 14 8 ababbabdcadcab 4 5 2 1 2 2 6 7 2 6 7 4 9 10 2 8 8 2 8 9 3 8 9 2 */