Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
0 3
Sample Output
aab
HINT
N<=5*10^5
T<2
K<=10^9
后缀自动机做这题似乎才是正解……
但是我不会后缀自动机啊= =
第一问的话后缀数组经典的做法。
按照rank下来,每次一个新的后缀产生了len-height[i]-sa[i]+1个新的子串。
比如
aaa
aabbb
那么当i=2,aabbb产生了(5-2-sa[2]+1)个新的子串。
用这个思想一直累加上去,直到K为止即可。
第二问我不太会……
看了大神的题解
他的思路就是枚举每一位是什么,然后二分判断K是否合法。
这一部分直接copy了他的代码……
我感觉会有更小渐进的后缀数组做法但是目前为止先放放吧。
这题竟然没T卡过了……
下次会了sam再来看看……
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int
N=500005;
char s[N];
int n,ans1,ans2,T,K;
int cnta[N],cntb[N],a[N],b[N<<1];
int tsa[N],sa[N],rank[N],height[N];
ll sum[N];
void Get_SA(){
for (int i=0;i<=25;i++) cnta[i]=0;
for (int i=1;i<=n;i++) cnta[s[i]-97]++;
for (int i=1;i<=25;i++) cnta[i]+=cnta[i-1];
for (int i=n;i;i--) sa[cnta[s[i]-97]--]=i;
rank[sa[1]]=1;
for (int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
for (int j=1;rank[sa[n]]!=n;j<<=1){
for (int i=1;i<=n;i++) a[i]=rank[i],b[i]=rank[i+j];
for (int i=0;i<=n;i++) cnta[i]=cntb[i]=0;
for (int i=1;i<=n;i++) cnta[a[i]]++,cntb[b[i]]++;
for (int i=1;i<=n;i++) cnta[i]+=cnta[i-1],cntb[i]+=cntb[i-1];
for (int i=n;i;i--) tsa[cntb[b[i]]--]=i;
for (int i=n;i;i--) sa[cnta[a[tsa[i]]]--]=tsa[i];
rank[sa[1]]=1;
for (int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]] || b[sa[i]]!=b[sa[i-1]]);
}
}
void Get_H(){
int len=0;
for (int i=1;i<=n;i++){
if (len) len--;
while (s[i+len]==s[sa[rank[i]-1]+len]) len++;
height[rank[i]]=len;
}
}
void solve1(){
for (int i=1;i<=n;i++){
int tmp=n-height[i]-sa[i]+1;
if (K>tmp) K-=tmp;
else{
ans1=sa[i];
ans2=sa[i]+height[i]+K-1;
return;
}
}
}
void solve2(){
sum[0]=(ll)0;
for (int i=1;i<=n;i++)
sum[i]=sum[i-1]+(ll)(n-sa[i]+1);
if (sum[n]<K) return;
int l1=1,r1=n,l2;
for (int i=1;i<=n;i++){
l2=l1;
for (int j='a';j<='z';j++){
int l=l2,r=r1;
while (l<=r){
int mid=(l+r)>>1;
if (s[sa[mid]+i-1]>j) r=mid-1;
else l=mid+1;
}
ll t=sum[r]-sum[l2-1]-(ll)(r-l2+1)*(ll)(i-1);
if (t>=K){
if (K<=r-l2+1){
ans1=sa[l2];
ans2=sa[l2]+i-1;
return;
}
l1=l2,r1=r;
K-=r-l2+1;
break;
}
l2=r+1,K-=t;
}
if (n-sa[l1]+1==i) l1++;
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
scanf("%d%d",&T,&K);
Get_SA(),Get_H();
ans1=ans2=-1;
if (!T) solve1();
else solve2();
if (ans1==-1){puts("-1");return 0;}
for (int i=ans1;i<=ans2;i++)
putchar(s[i]);
putchar('\n');
return 0;
}