题目描述
为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?
输入格式
第一行是一个仅由小写英文字母构成的字符串s
第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。
输出格式
输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。
输入输出样例
输入 #1
aabc
0 3
输出 #1
aab
输入 #2
aabc
1 3
输出 #2
aa
输入 #3
aabc
1 11
输出 #3
-1
说明/提示
数据范围
对于10%的数据,n ≤ 1000。
对于50%的数据,t = 0。
对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。
其实我对sam还不是很透彻只是似懂非懂
只放代码吧
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 2e6+5; // 两倍空间 , 一次插入可能产生两个节点
int n , tot = 1 , las = 1;
int len[N] , fa[N] , tr[N][26] , a[N] , t[N];
long long siz[N] , sum[N];
char c[N];
void Insert(int c) // 板子又一次出锅
{
int cur = ++tot , p = las; len[cur] = len[las] + 1; las = cur; siz[cur] = 1;
for( ; p && tr[p][c] == 0 ; p = fa[p]) tr[p][c] = cur;
if(p == 0) fa[cur] = 1;
else
{
int q = tr[p][c];
if(len[q] == len[p] + 1) fa[cur] = q;
else
{// 这里的nq的siz不能设为 1
int nq = ++tot;/*siz[tot] = 1;*/ fa[nq] = fa[q]; len[nq] = len[p] + 1;
for(int i = 0 ; i < 26 ; ++i) tr[nq][i] = tr[q][i];
fa[cur] = fa[q] = nq;
for( ; p && tr[p][c] == q ; p = fa[p]) tr[p][c] = nq;
}
}
}
int main()
{
scanf("%s",c+1); n = strlen(c+1);
int T , K; scanf("%d %d" , &T , &K);
for(int i = 1 ; i <= n ; ++i) Insert(c[i] - 'a');
for(int i = 1 ; i <= tot ; ++i) t[len[i]]++;
for(int i = 1 ; i <= tot ; ++i) t[i] += t[i-1];
for(int i = 1 ; i <= tot ; ++i) a[t[len[i]]--] = i;
// tr 与 fa 并不是一个东西 ,
// tr 表示在这个状态后加上 i 会到达的状态 ,
// fa 是后缀中第一个endpos不同于它本身的后缀的状态
for(int i = tot ; i >= 1 ; --i)
{
int x = a[i];
if(T == 1) siz[fa[x]] += siz[x];
else siz[x] = 1;
}
siz[1] = 0;
for(int i = tot ; i >= 1 ; --i)
{
int x = a[i]; sum[x] = siz[x];
for(int j = 0 ; j < 26 ; ++j)
if(tr[x][j]) sum[x] += sum[tr[x][j]];
}
if(sum[1] < K) puts("-1");
else
{
int now = 1; K -= siz[now];
while(K > 0)
{
int i = 0;
while(K > sum[tr[now][i]])
{
K -= sum[tr[now][i]];
i++;
}
now = tr[now][i]; putchar(i + 'a');
K -= siz[now];
}
}
return 0;
}