题目描述
给定一个字符串。
求这个字符串的字典序第 (k) 大的本质不同子串(不同位置只算一次)。
求这个字符串的字典序第 (k) 大的子串(不同位置算多次)。
正解
建出后缀自动机,把每一个点往后走有多少种方案求出来,就可以 (O(n)) 定位字符串了。
一个点往后走的方案等于所有后继节点(不仅仅是直接后继,还包括可以走到的节点)所代表的字符串的方案(或者说是出现次数)求和。
一个节点所代表的字符串的方案分下面两类讨论:
( exttt{t = 0}),出现多次算一次
在后缀自动机上面移动,走到一个字符串的方案唯一。
也就是说,所有移动的方案本质不同。
那么一个节点方案就是 (1)。
( exttt{t = 1}),出现多次算多次
自动机上一个点的方案设为它的出现次数((endpos) 集合大小)。
一个点 (endpos) 集合大小就是对其 (parent) 树的子树求个和(先把原字符串结尾位置对应的节点初始化为 (1))。
代码
/*
t = 0, 出现多次算一次
t = 1, 出现多次算多次
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, k, t;
int pos[N];
char s[N];
int last, vcnt;
int deg[N << 1];
struct node {
int link, len, siz;
int to[26];
} a[N << 1];
void extend(int c) {
int p = last, cur = ++vcnt;
a[cur].len = a[last].len + 1;
while(~p && !a[p].to[c]) {
a[p].to[c] = cur;
p = a[p].link;
}
if(p == -1) {
a[cur].link = 0;
} else {
int q = a[p].to[c];
if(a[q].len == a[p].len + 1) {
a[cur].link = q;
} else {
int clone = ++vcnt;
a[clone] = a[q];
a[clone].len = a[p].len + 1;
while(~p && a[p].to[c] == q) {
a[p].to[c] = clone;
p = a[p].link;
}
a[q].link = a[cur].link = clone;
}
}
last = cur;
}
long long f[N << 1];
long long getFunc(int u) {
if(f[u] != -1) return f[u];
long long &F = f[u]; F = a[u].siz;
for(int c = 0, v; c < 26; ++c) {
v = a[u].to[c];
if(v == 0) continue;
F += getFunc(v);
}
return F;
}
void dfs(int u, int rem) {
rem -= a[u].siz;
if(rem <= 0) return void();
for(int c = 0, v; c < 26; ++c) {
v = a[u].to[c];
if(v == 0) continue;
if(rem > f[v]) {
rem -= f[v];
} else {
putchar('a' + c);
dfs(v, rem);
break;
}
}
}
int main() {
scanf("%s %d %d", s + 1, &t, &k);
n = strlen(s + 1);
a[last = 0].link = -1;
for(int i = 1; i <= n; ++i) {
extend(s[i] - 'a');
pos[i] = last;
}
if(!t) {
for(int i = 1; i <= vcnt; ++i)
a[i].siz = 1;
} else {
for(int i = 1; i <= n; ++i)
++a[pos[i]].siz;
for(int i = 1; i <= vcnt; ++i)
++deg[a[i].link];
queue<int> Q;
for(int i = 1; i <= vcnt; ++i)
if(deg[i] == 0)
Q.push(i);
while(!Q.empty()) {
int u = Q.front(), v = a[u].link; Q.pop();
if(v == 0) continue;
a[v].siz += a[u].siz;
if(--deg[v] == 0) Q.push(v);
}
}
memset(f, -1, sizeof f);
getFunc(0);
if(f[0] < k) {
puts("-1");
} else {
dfs(0, k);
putchar('
');
}
return 0;
}