题目
给定一个长度为
N
N
的字符串,有两种问法。
第一种:问第
K
K
小的本质不同的子串。
第二种:问第小的子串。
本质不同,也就是说如果同一个子串在不同的位置出现了,不重复计算。
N≤105
N
≤
10
5
。
题解
所谓子串,是后缀的前缀。
Trie?Trie不能够在复杂度允许的情况下识别所有的子串。
SA?对于第一种问法显然可以做。但是搞不定第二种。
那么有没有什么能够一下子识别所有的子串,且能够维护相同的子串出现的次数?
可以用SAM。
在SAM的转移图中,从起点走到转移图中的任一点的路径,表示任一个子串。每个点的
right
r
i
g
h
t
集恰好表示了一个子串出现的次数。
那么怎么
O(|S|)
O
(
|
S
|
)
维护
right
r
i
g
h
t
集的大小?
设
sum[x]
s
u
m
[
x
]
表示
x
x
点的集大小。
一开始在建SAM时,
sum[np]=1
s
u
m
[
n
p
]
=
1
,表示新增状态的
right
r
i
g
h
t
集中的元素增加了
|S|
|
S
|
一个元素。
利用
fail
f
a
i
l
树,
sum[fail[x]]+=sum[x]
s
u
m
[
f
a
i
l
[
x
]
]
+
=
s
u
m
[
x
]
,表示
fail[x]
f
a
i
l
[
x
]
对应的子串的出现次数必然包含
x
x
对应的子串的出现次数。
,任意的
fail[x]=fail[y]
f
a
i
l
[
x
]
=
f
a
i
l
[
y
]
,
right[x]
r
i
g
h
t
[
x
]
与
right[y]
r
i
g
h
t
[
y
]
的任意元素都互不相同。所以以上的算法是正确的。
设
su[x]
s
u
[
x
]
表示以
x
x
对应的子串为前缀的所有子串在中出现的次数,则像权值线段树那样子跑一遍求出具体的答案。
对于第一种问法?
强行让
sum[x]=1
s
u
m
[
x
]
=
1
,其他步骤按照上面做。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 500010
#define M 1000010
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int to,next;
};note edge[M];
int tot,head[M];
int i,j,k,K,l,r,x,n,m,t,temp;
int gs,wz;
int Len[M],fail[M],qu[M];
int sum[M],su[M],o[M];
int sam[M][26];
char ch,s[N];
void lb(int x,int y){edge[++tot].to=y;edge[tot].next=head[x];head[x]=tot;}
int ins(int x,int wz){
int i,np=++gs,p=wz,nq,q;
Len[np]=Len[p]+1;sum[np]++;
while(p&&!sam[p][x])sam[p][x]=np,p=fail[p];
if(!p){fail[np]=1;return np;}
q=sam[p][x];
if(Len[q]==Len[p]+1)fail[np]=q;else{
nq=++gs;
fo(i,0,25)sam[nq][i]=sam[q][i];
fail[nq]=fail[q];
fail[np]=fail[q]=nq;
Len[nq]=Len[p]+1;
while(p&&sam[p][x]==q)sam[p][x]=nq,p=fail[p];
}
return np;
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
ch=getchar();
while(ch>='a'&&ch<='z')s[++n]=ch,ch=getchar();
scanf("%d%d",&t,&K);
gs=wz=1;
fo(i,1,n)
wz=ins(s[i]-'a',wz);
if(!t)fo(i,2,gs)sum[i]=1;else{
fo(i,2,gs)lb(fail[i],i);
qu[r=1]=1;l=0;
while(l<r){
x=qu[++l];
for(i=head[x];i;i=edge[i].next)qu[++r]=edge[i].to;
}
fd(i,gs,2)sum[fail[qu[i]]]+=sum[qu[i]];
sum[1]=0;
}
fo(i,1,gs)fo(j,0,25)if(sam[i][j])o[sam[i][j]]++;
qu[r=1]=1;l=0;
while(l<r){
i=qu[++l];
fo(j,0,25)if(sam[i][j]){
o[sam[i][j]]--;
if(!o[sam[i][j]])qu[++r]=sam[i][j];
}
}
fd(i,gs,1){
fo(j,0,25)su[qu[i]]+=su[sam[qu[i]][j]];
su[qu[i]]+=sum[qu[i]];
}
if(K>su[1]){puts("-1");return 0;}
wz=1;
while(1){
if(sum[wz]>=K)break;
temp=sum[wz];
fo(i,0,25)
if(temp+su[sam[wz][i]]<K)temp+=su[sam[wz][i]];
else break;
putchar(i+'a');
K-=temp;
wz=sam[wz][i];
}
return 0;
}