Luogu P3975 [TJOI2015]弦论
求第\(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]]\)。
直到\(k\le 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;
}