题目描述
https://www.luogu.org/problem/P3975
为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?
输入格式
第一行是一个仅由小写英文字母构成的字符串s
第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。
输出格式
输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。
输入输出样例
输入 #1复制
aabc 0 3
输出 #1复制
aab
输入 #2复制
aabc 1 3
输出 #2复制
aa
输入 #3复制
aabc 1 11
输出 #3复制
-1
说明/提示
数据范围
对于10%的数据,n ≤ 1000。
对于50%的数据,t = 0。
对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。
利用后缀自动机求解
endpos 大小就是 有多少个这样的子串
按照parent 反拓扑序 求 sum [ i ] (经过自动机每个节点的子串数目)
关于反拓扑序的解释
将所有的parent 反过来,我们就得到了parent树
如果要处理什么,就需要parent树的拓扑序
(因为parent相当于包含了所有的他的子树,都需要更新上去)
其实不需要拓扑排序
我们知道s 的endpos完全被parent 的endpos包含
s.longest一定长于parent.longest
所以,一个状态的longest越长,它一定要被更先访问
所以,按照longest 的长度进行桶排序就可以解决拓扑序了
求出来之后按照字典序遍历即可
参考
https://www.cnblogs.com/cjyyb/p/8446205.html
#include<cstring>
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
struct SAM
{
int trans[maxn<<1][26],slink[maxn<<1],maxlen[maxn<<1];
int last,now,root,len;
int indegree[maxn<<1],endpos[maxn<<1],rank[maxn<<1],ans[maxn<<1];
void newnode(int v)
{
maxlen[++now]=v;
}
void extend(int c)
{
newnode(maxlen[last]+1);
int p=last,np=now;
while(p&&!trans[p][c])
{
trans[p][c]=np;
p=slink[p];
}
if(!p)
slink[np]=root;
else
{
int q=trans[p][c];
if(maxlen[p]+1!=maxlen[q])
{
newnode(maxlen[p]+1);
int nq=now;
memcpy(trans[nq],trans[q],sizeof(trans[q]));
slink[nq]=slink[q];
slink[q]=slink[np]=nq;
while(p&&trans[p][c]==q)
{
trans[p][c]=nq;
p=slink[p];
}
}
else
slink[np]=q;
}
last=np;
endpos[np]=1;
}
void build(string s)
{
root=last=now=1;
memset(trans,0,sizeof(trans));
memset(slink,0,sizeof(slink));
memset(maxlen,0,sizeof(maxlen));
len=s.size();
for(int i=0;i<len;++i)
extend(s[i]-'a');
}
inline void topsort()
{
for(int i=1;i<=now;++i)
indegree[maxlen[i]]++;
for(int i=1;i<=now;++i)
indegree[i]+=indegree[i-1];
for(int i=1;i<=now;++i)
rank[indegree[maxlen[i]]--]=i;
for(int i=now;i>=1;--i)
{
int x=rank[i];
endpos[slink[x]]+=endpos[x];
}
}
}sam;
ll sum[maxn<<1];
void f(int x,int k)
{
if(k<=sam.endpos[x])
return ;
k-=sam.endpos[x];
for(int i=0;i<26;i++)
{
if(sam.trans[x][i])
{
if(k>sum[sam.trans[x][i]])
{
k-=sum[sam.trans[x][i]];
continue;
}
else
{
printf("%c",i+'a');
f(sam.trans[x][i],k);
return ;
}
}
}
}
int main()
{
string s;
cin>>s;
int t,k;
cin>>t>>k;
sam.build(s);
sam.topsort();
if(t==0)
{
for(int i=sam.root;i<=sam.now;i++)
{
sum[i]=1;
sam.endpos[i]=1;
}
}
else if(t==1)
{
for(int i=sam.root;i<=sam.now;i++)
{
sum[i]=sam.endpos[i];
}
}
sum[1]=0;
sam.endpos[1]=0;
for(int i=sam.now;i>=sam.root;i--)
{
for(int j=0;j<26;j++)
{
if(sam.trans[sam.rank[i]][j])
sum[sam.rank[i]]+=sum[sam.trans[sam.rank[i]][j]];
}
}
if(sum[sam.root]<k)
printf("-1");
else
f(1,k);
return 0;
}