Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
题解:
这里介绍SA的做法(相对SAM会慢一些)
先处理出有多少个不相同的子串,对于T=0的询问,直接找第k小子串即可
对于T=1的询问,二分答案是第几小的串,找有多少个比他小的串(位置不同算作多个)
至于怎么找有多少个串比某个串小,假设当前的串在SA上排
i
,长度为
出现的错误
str赋值时-‘a’不+1在求height值时有边界问题
没有用longlong
(我的代码T了,优化了一天的常数还是T,但是要了数据本地评测是能过的)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<vector>
#include<string>
#include<bitset>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
const int maxn = 510000;
int t[maxn],fir[maxn],sec[maxn];
int sa[maxn],rank[maxn],height[maxn];
ll sl[maxn],sh[maxn];
void sort_(int *ret,int *rk,int *str,int n,int m)
{
for(int i=0;i<=m;i++)t[i]=0;
for(int i=1;i<=n;i++)t[str[rk[i]]]++;
for(int i=1;i<=m;i++)t[i]+=t[i-1];
for(int i=n;i>=1;i--)ret[t[str[rk[i]]]--]=rk[i];
}
int str[maxn];
char st[maxn];
void get_sa(int n,int m)
{
for(int i=1;i<=n;i++) rank[i]=i;
//sort_(sa,rank,str,n,m);
for(int i=0;i<=m;i++)t[i]=0;
for(int i=1;i<=n;i++)t[str[rank[i]]]++;
for(int i=1;i<=m;i++)t[i]+=t[i-1];
for(int i=n;i>=1;i--)sa[t[str[rank[i]]]--]=rank[i];
rank[sa[1]]=1;
for(int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
int k=rank[sa[n]],pw=1;
while(k!=n)
{
for(int i=1;i<=n;i++)
{
sa[i]=i;
fir[i]=rank[i];
sec[i]=i+pw>n?0:rank[i+pw];
}
//sort_(rank,sa,sec,n,n);
//sort_(sa,rank,fir,n,n);
for(int i=0;i<=n;i++)t[i]=0;
for(int i=1;i<=n;i++)t[sec[sa[i]]]++;
for(int i=1;i<=n;i++)t[i]+=t[i-1];
for(int i=n;i>=1;i--)rank[t[sec[sa[i]]]--]=sa[i];
for(int i=0;i<=n;i++)t[i]=0;
for(int i=1;i<=n;i++)t[fir[rank[i]]]++;
for(int i=1;i<=n;i++)t[i]+=t[i-1];
for(int i=n;i>=1;i--)sa[t[fir[rank[i]]]--]=rank[i];
rank[sa[1]]=1;
for(int i=2;i<=n;i++)
rank[sa[i]]=rank[sa[i-1]]+
(fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]]);
k=rank[sa[n]];pw<<=1;
}
}
void get_height(int n)
{
height[1]=0;int k=0;
for(int i=1;i<=n;i++)
{
if(k)k--;
if(rank[i]==1)continue;
while(str[i+k]==str[sa[rank[i]-1]+k])k++;
height[rank[i]]=k;
}
}
int n,m,qx,qy;
ll num;
void find_(int k,int &id,int &len)
{
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(k<=sh[mid])r=mid-1;
else l=mid+1;
}
len=k-sh[r]+height[r+1];
id=r+1;
}
bool judge(int k)
{
int tx, ty;
find_(k,tx,ty);
ll ret=ty+sl[tx-1];
int hi=ty;
for(int i=tx+1;i<=n;i++)
{
if(height[i]<hi)hi=height[i];
if(hi==0) break;
ret+=hi;
}
if(ret>=qy)return true;
else return false;
}
int C()
{
int l=1,r=qy;
while(l<=r)
{
int mid=(l+r)>>1;
if(judge(mid))r=mid-1;
else l=mid+1;
}
return r+1;
}
int main()
{
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
scanf("%s",st); n=strlen(st);
scanf("%d%d",&qx,&qy);
for(int i=1;i<=n;i++)str[i]=st[i-1]-'a'+1;
get_sa(n,28);
get_height(n);
for(int i=1;i<=n;i++)sl[i]=sl[i-1]+n-sa[i]+1;
for(int i=1;i<=n;i++)sh[i]=sh[i-1]+n-sa[i]+1-height[i];
num=sh[n];
int tx,ty;
int ans;
if(qx==0)
{
if(qy>num){printf("-1\n");return 0;}
find_(qy,tx,ty);
}
else
{
ans=C();
if(ans>num){printf("-1\n");return 0;}
find_(ans,tx,ty);
}
for(int i=sa[tx];i<=sa[tx]+ty-1;i++)printf("%c",st[i-1]);
printf("\n");
return 0;
}