洛谷传送门
BZOJ传送门
题目描述
为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为 n n 的字符串,求出它的第小子串是什么。你能帮帮她吗?
输入输出格式
输入格式:
第一行是一个仅由小写英文字母构成的字符串 s s
第二行为两个整数和 k k ,为 0 0 则表示不同位置的相同子串算作一个,为 1 1 则表示不同位置的相同子串算作多个。的意义见题目描述。
输出格式:
输出数据仅有一行,该行有一个字符串,为第 k k 小的子串。若子串数目不足个,则输出 −1 − 1 。
输入输出样例
输入样例#1:
aabc
0 3
输出样例#1
aab
输入样例#2:
aabc
1 3
输出样例#2:
aa
输入样例#3:
aabc
1 11
输出样例#3:
-1
说明
数据范围
对于10%的数据, n≤1000 n ≤ 1000 。
对于50%的数据, t=0 t = 0 。
对于100%的数据, n≤5×105,t<2,k≤109 n ≤ 5 × 10 5 , t < 2 , k ≤ 10 9 。
解题分析
SAM S A M 的板题。 后缀自动机是个 DAG D A G , 所以我们构建出 parent p a r e n t 链后从下往上按拓扑序递推, 得到每个子串出现的次数, 再通过转移边做一遍类似前缀和的操作得到在每个点向下走会有多少个子串。
如果 t=0 t = 0 , 那么每个点的贡献都为 1 1 。 如果, 那么贡献为 right r i g h t 集合大小。 因为我们在 insert i n s e r t 操作的时候分离出来的那个辅助点实际上和原来的点表示一个子串, 所以其 siz s i z 初始化为0,否则向上累加的时候会多算次数,但当 t=0 t = 0 的时候作为一个单独的子串其 siz=1 s i z = 1 。(具体画画图就知道了)。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MX 2000500
char dat[MX];
int to[MX][26], par[MX], siz[MX], len[MX], buc[MX], id[MX], sum[MX];
int cnt, l, arr, last, cur, typ, k;
namespace SAM
{
IN void insert(R int id)
{
R int now, tar;
cur = ++cnt; len[cur] = len[last] + 1; siz[cur] = 1;
for (now = last; ~now; now = par[now])
{
if(to[now][id]) break;
to[now][id] = cur;
}
if(now < 0) return last = cur, par[cur] = 0, void();
tar = to[now][id];
if(len[tar] == len[now] + 1) return last = cur, par[cur] = tar, void();
int nw = ++cnt; std::memcpy(to[nw], to[tar], sizeof(to[tar]));
par[nw] = par[tar], par[tar] = nw; len[nw] = len[now] + 1;
for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
par[cur] = nw; last = cur;
}
void calc()
{
for (R int i = 1; i <= cnt; ++i) ++buc[len[i]];
for (R int i = 1; i <= l; ++i) buc[i] += buc[i - 1];
for (R int i = 1; i <= cnt; ++i) id[buc[len[i]]--] = i;
for (R int i = cnt; i; --i)
{
if(typ) siz[par[id[i]]] += siz[id[i]];
else siz[id[i]] = 1;
}
siz[0] = 0;
for (R int i = cnt; ~i; --i)
{
sum[id[i]] = siz[id[i]];
for (R int j = 0; j < 26; ++j)
if(to[id[i]][j]) sum[id[i]] += sum[to[id[i]][j]];
}
}
}
int main(void)
{
par[0] = -1;
scanf("%s", dat + 1);
l = std::strlen(dat + 1);
scanf("%d%d", &typ, &k);
for (R int i = 1; i <= l; ++i) SAM::insert(dat[i] - 'a');
SAM::calc();
if(sum[0] < k) return puts("-1"), 0;
R int now = 0, tar;
W ((k -= siz[now]) > 0)
{
for (tar = 0; tar < 26; ++tar)
{
if(to[now][tar])
{
if(k <= sum[to[now][tar]]) break;
k -= sum[to[now][tar]];
}
}
now = to[now][tar];
putchar('a' + tar);
}
}