Description
对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。
Output
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input
aabc
0 3
Sample Output
aab
HINT
N<=5*10^5
T<2
K<=10^9
题解
SAM入门题,巩固了一下对于right集合构造的理解
首先对原串建一个SAM,然后通过拓扑序把right集合构建出来
for(int i=1;i<=cnt;i++)Rsort[tr[i].dep]++;
for(int i=1;i<=len;i++)Rsort[i]+=Rsort[i-1];
for(int i=cnt;i>=1;i--)sa[Rsort[tr[i].dep]--]=i;
这三行是一个优化的拓扑序,用了基数排序的思想
你可以贪心地想一想,原串中len越大的点,他们的拓扑序就在越后面。然后对于编号越大的点,拓扑序会比len相同但是编号小一点的点的拓扑序大
处理T不同的情况
对于T=0,right集合就搞成1,因为你出现的次数是right,但是相同的看成一个,我们就把他搞成1就好了
对于T=1,right集合不变
之后处理一下这个点出来能跑出多少不同的串,在SAM上搜一下即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct SAM
{
int son[30],dep,parent;
}tr[1110000];int cnt,last,root;
char ch[510000];
void add(int k)
{
int x=ch[k]-'a';
int p=last,np=++cnt;
tr[np].dep=k;
while(p && tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent;
if(p==0)tr[np].parent=root;
else
{
int q=tr[p].son[x];
if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
else
{
int nq=++cnt;
tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1;
tr[q].parent=tr[np].parent=nq;
while(p && tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
}
}
last=np;
}
int T,k;
int Rsort[1110000],right[1110000],sa[1110000],sum[1110000];
void dfs(int x)
{
if(k<=right[x])return ;
k-=right[x];
for(int i=0;i<=25;i++)
if(tr[x].son[i]!=0)
{
int y=tr[x].son[i];
if(k>sum[y])k-=sum[y];
else {printf("%c",i+'a');dfs(y);return ;}
}
}
int main()
{
root=last=cnt=1;
scanf("%s",ch+1);int len=strlen(ch+1);
for(int i=1;i<=len;i++)add(i);
scanf("%d%d",&T,&k);
for(int i=1;i<=cnt;i++)Rsort[tr[i].dep]++;
for(int i=1;i<=len;i++)Rsort[i]+=Rsort[i-1];
for(int i=cnt;i>=1;i--)sa[Rsort[tr[i].dep]--]=i;
for(int i=1,p=root;i<=len;i++)
{
int x=ch[i]-'a';
p=tr[p].son[x];
right[p]++;
}
for(int i=cnt;i>=1;i--)
{
if(T==1)right[tr[sa[i]].parent]+=right[sa[i]];
else right[sa[i]]=1;
}
right[root]=0;
for(int i=cnt;i>=1;i--)
{
int now=sa[i];sum[now]=right[now];
for(int j=0;j<=25;j++)
if(tr[now].son[j]!=0)sum[now]+=sum[tr[now].son[j]];
}
if(k>sum[root])printf("-1\n");
else dfs(root);
return 0;
}