题目大意:
给你一个字符串 S ( ∣ S ∣ ≤ 1 e 5 ) S(|S| \leq 1e5) S(∣S∣≤1e5).然后给你 Q ( Q ≤ 1 e 5 ) Q(Q\leq1e5) Q(Q≤1e5)次询问.每次询问一个区间子串 [ L , R ] [L,R] [L,R],记作 S ′ S' S′.问你该子串从左到右的第 k k k次出现的位置.
题目思路:
前置知识: O ( l o g n ) O(logn) O(logn)求一个子串在总串中出现的次数.
由于一个子串一定是一个后缀的前缀.所以子串在后缀排序后出现也一定是连续的.
对 h e i g h t height height数组建ST表,在 r k [ L ] rk[L] rk[L]处向两边二分找最远的 min { h e i g h t } ≥ R − L + 1 \min \{height\} \geq R-L+1 min{height}≥R−L+1的区间范围(就是一个求 L C P LCP LCP的过程).这个时候的区间就是该子串所出现的所有次数了.
现在题目要求这些子串的第 k k k次出现的位置.我们可以对SA数组建主席树.求解区间第 k k k大即可.
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ri register int
const int maxn = 1e5 + 5;
namespace SA
{
char s[maxn<<1];
int sa[maxn<<1],rk[maxn<<1],sec[maxn<<1],tax[maxn<<1] , n , m;
int ht[maxn];
void init (){
for (int i = 0 ; i <= 2 * n ; i++)
s[i] = sa[i] = rk[i] = sec[i] = tax[i] = 0;
}
void Solve (char a[] , int len){
// 记得当数组是整数数组时, m = n + 5
m = 300/*字符ascii码最大取值*/ , n = len;
init();
for (int i = 1 ; i <= n ; i++) s[i] = a[i];
int *rnk=rk,*sc=sec;
for(ri i=1;i<=m;++i) tax[i]=0;
for(ri i=1;i<=n;++i) ++tax[rnk[i]=s[i]];
for(ri i=1;i<=m;++i)tax[i]+=tax[i-1];
for(ri i=n;i>=1;--i)sa[tax[rnk[i]]--]=i;
for(ri k=1,p=0;k<=n;k<<=1,p=0){
for(ri i=n-k+1;i<=n;++i)sc[++p]=i;
for(ri i=1;i<=n;++i) if(sa[i]>k)sc[++p]=sa[i]-k;
for(ri i=1;i<=m;++i) tax[i]=0;
for(ri i=1;i<=n;++i) ++tax[rnk[sc[i]]];
for(ri i=1;i<=m;++i) tax[i]+=tax[i-1];
for(ri i=n;i>=1;--i) sa[tax[rnk[sc[i]]]--]=sc[i];
swap(rnk,sc);p=rnk[sa[1]]=1;
for(ri i=2;i<=n;++i)rnk[sa[i]]=(sc[sa[i]]==sc[sa[i-1]]&&sc[sa[i]+k]==sc[sa[i-1]+k]?p:++p);//debug(rnk,sc);
if(p>=n)break;else m=p;
}
for(ri i=1;i<=n;++i)rk[sa[i]]=i;
}
void getHeight()
{
int i , k;
for (i = 1, k = 0; i <= n; ++i) {
if (k) --k;
while (s[i + k] == s[sa[rk[i] - 1] + k]) ++k;
ht[rk[i]] = k;
}
}
}
int minn[21][maxn] , Log2[maxn];
void st(int n){
Log2[1] = 0;
for (int i = 2 ; i <= n ; i++)
Log2[i] = Log2[i >> 1] + 1;
for(int i=1;i<=20;i++)
for(int j=1;j+(1<<i)<=n+1;j++)
minn[i][j]=min(minn[i-1][j],minn[i-1][j+(1<<(i-1))]);
}
int query_minn(int l,int r){
int k=Log2[r-l+1];
return min(minn[k][l],minn[k][r-(1<<k)+1]);
}
#define mid ((l + r) >> 1)
int sum[maxn << 5] , ls[maxn << 5] , rs[maxn << 5] , rt[maxn] , tot;
int add (int l , int r , int t , int p , int c)
{
int now = ++tot;
ls[now] = ls[t];
rs[now] = rs[t];
sum[now] = sum[t] + c;
if (l == r) return now;
if (p <= mid) ls[now] = add(l , mid , ls[now] , p , c);
else rs[now] = add(mid + 1 , r , rs[now] , p , c);
return now;
}
int ask (int u , int v , int l , int r , int k)
{
if (l == r) return l;
int d = sum[ls[v]] - sum[ls[u]];
if (d >= k) return ask(ls[u] , ls[v] , l , mid , k);
return ask(rs[u] , rs[v] , mid + 1 , r , k - d);
}
char a[maxn];
int main()
{
int t; scanf("%d" , &t);
while (t--){
int n , q;scanf("%d%d" , &n , &q);
SA::init();
scanf("%s" , a + 1);
SA::Solve(a , n);
SA::getHeight();
for (int i = 1; i <= n ; i++){
minn[0][i] = SA::ht[i];
}
st(n);
tot = rt[0] = 0;
for (int i = 1; i <= n ; i++){
rt[i] = add(1 , n , rt[i - 1] , SA::sa[i] , 1);
}
while (q--){
int x , y , k; scanf("%d%d%d" , &x , &y , &k);
int len = y - x + 1;
int l = SA::rk[x] + 1 , r = n;
while (l <= r){
int md = ((l + r) >> 1);
if (query_minn(SA::rk[x] + 1 , md) >= len) l = md + 1;
else r = md - 1;
}
int wl , wr;
wr = r;
l = 1 , r = SA::rk[x];
while (l <= r){
int md = ((l + r) >> 1);
if (query_minn(md , SA::rk[x]) >= len) r = md - 1;
else l = md + 1;
}
wl = l - 1;
if (wr - wl + 1 < k){
printf("-1\n");
continue;
}
printf("%d\n" , ask(rt[wl - 1] , rt[wr] , 1 , n , k));
}
}
return 0;
}