今年CCPC的第一場網絡賽,到現在才補掉。。。
當時還沒學後綴數組,AC自動機,然後賽場上覺得這題一定是用其中一個算法做的,搜AC自動機是幹什麼的,偶然就查到一道類似的可以解決這種題的模板,然後直接學習AC自動機,魔改模板,還過了樣例,最後wa了一下午,現在還不知道爲什麼wa,大概是AC自動機的trie樹根本建不下吧。
題意:給你一個串,q個詢問,問你這l到r的子串在這個串中出現了k次的起始位置
題解:
先跑後綴數組,得到height數組。我們知道,在height[]中,一段值大於等於len的最大區間長度+1就是這個長len的子串在串中出現了幾次,+1是因爲區間最左邊l-1是排序後第一次出現這個子串的位置,所以在之後的st表查詢最小值時要l++(看代碼能發現)
我們用st表建立height數組的區間最小值,這樣子我們就能通過二分求出一段值大於等於len的最大區間長度,即這個子串出現的初始位置,和最終位置。
對sa數組做主席樹,通過上面求出的區間l,r來找尋l到r的第k大的數。
注意多組樣例清空的問題。
#include<bits/stdc++.h>
#define rint register int
#define inv inline void
#define ini inline int
#define maxn 300050
using namespace std;
typedef long long ll;
const ll mod=1000000007;
char s[maxn];
int y[maxn],x[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn];
int n,m,k;
inv get_SA() {
for(int i=1;i<=m;i++) c[i]=0;
for (rint i=1; i<=n; ++i) ++c[x[i]=s[i]];
for (rint i=2; i<=m; ++i) c[i]+=c[i-1];
for (rint i=n; i>=1; --i) sa[c[x[i]]--]=i;
for (rint k=1; k<=n; k<<=1) {
rint num=0;
for (rint i=n-k+1; i<=n; ++i) y[++num]=i;
for (rint i=1; i<=n; ++i) if (sa[i]>k) y[++num]=sa[i]-k;
for (rint i=1; i<=m; ++i) c[i]=0;
for (rint i=1; i<=n; ++i) ++c[x[i]];
for (rint i=2; i<=m; ++i) c[i]+=c[i-1];
for (rint i=n; i>=1; --i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1;
num=1;
for (rint i=2; i<=n; ++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
if (num==n) break;
m=num;
}
}
inv get_height() {
rint k=0;
for (rint i=1; i<=n; ++i) rk[sa[i]]=i;
for (rint i=1; i<=n; ++i) {
if (rk[i]==1) continue;//第一名height爲0
if (k) --k;//h[i]>=h[i-1]-1;
rint j=sa[rk[i]-1];
while (j+k<=n && i+k<=n && s[i+k]==s[j+k]) ++k;
height[rk[i]]=k;//h[i]=height[rk[i]];
}
}
int mmin[maxn][20];
void RMQ_ST(){
for(int i=1;i<=n;i++){
mmin[i][0]=height[i];
}
int end_j=log(n+0.0)/log(2.0);
int end_i;
for(int j=1;j<=end_j;j++){
end_i=n+1-(1<<j);
for(int i=1;i<=end_i;i++){
mmin[i][j]=min(mmin[i][j-1],mmin[i+(1<<(j-1))][j-1]);
}
}
}
int QueryMin(int L,int R){
if(L>R) swap(L,R);
if(L==R) return n-sa[L]+1;
L++;
int k=log(R-L+1.0)/log(2.0);
return min(mmin[L][k],mmin[R-(1<<k)+1][k]);
}
struct Seg {
int l, r, sum;
} tr[maxn*40];
int cnt,root[maxn];
void build(int &root,int l,int r){
root=++cnt;
tr[root].sum=0;
if(l==r) return;
int mid=l+r>>1;
build(tr[root].l,l,mid);
build(tr[root].r,mid+1,r);
}
void update(int &x,int y,int l,int r,int pos,int val){
int pre=x;
x=++cnt;
tr[x]=pre?tr[pre]:tr[y];
if(l==r){
tr[x].sum=1;
return;
}
int mid=l+r>>1;
if(pos<=mid) update(tr[x].l,tr[y].l,l,mid,pos,val);
else update(tr[x].r,tr[y].r,mid+1,r,pos,val);
tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int query(int x,int y,int l,int r,int k){
if(tr[y].sum-tr[x].sum<k) return -1;
if(l==r) return l;
int mid=l+r>>1;
if(tr[tr[y].l].sum-tr[tr[x].l].sum>=k) return query(tr[x].l,tr[y].l,l,mid,k);
else return query(tr[x].r,tr[y].r,mid+1,r,k-(tr[tr[y].l].sum-tr[tr[x].l].sum));
}
int getl(int x,int len){
int l=1,r=x;
int ans=x;
while(l<=r){
int mid=l+r>>1;
if(QueryMin(mid,x)>=len){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
return ans;
}
int getr(int x,int len){
int l=x,r=n;
int ans=x;
while(l<=r){
int mid=l+r>>1;
if(QueryMin(x,mid)>=len){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
int main() {
int T;
scanf("%d",&T);
while(T--){
int q;
memset(root,0,sizeof(root));
memset(mmin,0,sizeof(mmin));
memset(height,0,sizeof(height));
cnt=0;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
int len=strlen(s+1);
m=200;
get_SA();
get_height();
build(root[0],1,n);
for(int i=1;i<=n;i++){
update(root[i],root[i-1],1,n,sa[i],1);
}
RMQ_ST();
while(q--){
int l,r,tl,tr;
scanf("%d%d%d",&l,&r,&k);
tl=getl(rk[l],r-l+1);
tr=getr(rk[l],r-l+1);
//printf("!%d %d\n",tl,tr);
ll ans;
if(tr-tl+1<k) ans=-1;
else ans=query(root[tl-1],root[tr],1,n,k);
printf("%lld\n",ans);
}
}
}