求第(k)小子串。
可以用后缀自动机求解。
(t=0),不同位置的相同子串算作一个。设(cnt[i])表示(i)的(endpos)集合大小,即结束位置为(i)的子串个数。因为相同子串只算一次,所以直接设所有点的(cnt=1)即可。
(t=1),不同位置的相同字串算作多个。这时正常维护(cnt[i] = cnt[i]+sum cnt[j](i=fa[j]))即可。
设(f[i])表示经过(i)的子串数量。
(f[i] = cnt[i] + sum f[j](i=fa[j]))
按拓扑序从后往前更新一遍(parent)树。
找第(k)小子串:
从根节点开始遍历。设(x)表示下一个字符,枚举(a-z)。
若(k>f[ch[x]]),则说明要找的子串不在(ch[x])的子树上,(k)减去该子树大小,(k-f[ch[x]])。
否则,说明在(ch[x])的子树上,输出字符(x),走到节点(ch[x]),(k)减去这个节点自身的大小,(k-cnt[ch[x]])。
直到(kle 0)时,说明当前子串已经找到,退出。
注意:
- 最后可能会有(k<0),因为一个节点上可能有多个子串。
- 复制节点时,不要忘记设置新节点的(cnt=0)。
(code)
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;
const int maxn = 5e5+10;
int t,k,b[maxn<<1],rk[maxn<<1],f[maxn<<1];
char s[maxn];
struct SuffixAutomaton {
struct node {
int ch[26],fa,len,cnt;
void clean() {
memset(ch,0,sizeof(ch));
fa = len = cnt = 0;
}
} S[maxn<<1];
int root,last,siz;
void init() {
for(int i = 1; i <= siz; i++)
S[i].clean();
root = last = siz = 1;
}
void insert(int c) {
int p = last, now = ++siz;
S[now].cnt = 1;
S[now].len = S[p].len+1;
for(; p && !S[p].ch[c]; p = S[p].fa)
S[p].ch[c] = now;
if(!p) S[now].fa = root;
else {
int q = S[p].ch[c];
if(S[q].len == S[p].len+1)
S[now].fa = q;
else {
int q_new = ++siz;
S[q_new] = S[q];
S[q_new].cnt = 0;
S[q_new].len = S[p].len+1;
S[now].fa = S[q].fa = q_new;
for(; p && S[p].ch[c] == q; p = S[p].fa)
S[p].ch[c] = q_new;
}
}
last = now;
}
void calc() {
for(int i = 1; i <= siz; i++)
b[S[i].len]++;
for(int i = 1; i <= siz; i++)
b[i] += b[i-1];
for(int i = 1; i <= siz; i++)
rk[b[S[i].len]--] = i;
for(int i = siz; i; i--)
if(t) S[S[rk[i]].fa].cnt += S[rk[i]].cnt;
else S[rk[i]].cnt = 1;
S[root].cnt = 0;
for(int i = siz; i; i--) {
f[rk[i]] = S[rk[i]].cnt;
for(int j = 0; j < 26; j++)
f[rk[i]] += f[S[rk[i]].ch[j]];
}
}
void solve() {
if(k > f[root]) {
printf("-1");
return;
}
int now = root;
while(k > 0) {
for(int i = 0; i < 26; i++) {
if(!S[now].ch[i]) continue;
if(k > f[S[now].ch[i]])
k -= f[S[now].ch[i]];
else {
now = S[now].ch[i];
k -= S[now].cnt;
printf("%c",i+'a');
break;
}
}
}
}
} SAM;
int main() {
scanf("%s",s+1);
scanf("%d%d",&t,&k);
int n = strlen(s+1);
SAM.init();
for(int i = 1; i <= n; i++)
SAM.insert(s[i]-'a');
SAM.calc();
SAM.solve();
return 0;
}