[BZOJ3998][TJOI2015]弦论
[BZOJ3998][TJOI2015]弦论
试题描述
对于一个给定长度为N的字符串,求它的第K小子串是什么。
输入
第一行是一个仅由小写英文字母构成的字符串S
第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述
输出
输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
输入示例
aabc 0 3
输出示例
aab
数据规模及约定
N<=5*10^5
T<2
K<=10^9
题解
对于 T = 0 的情况,就是这道题;对于 T = 1 的情况就是把每个节点的权值由 1 变成 right 集合的大小。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1000010 #define maxa 26 char S[maxn]; int len, T, K; int rt, last, ToT, to[maxn][maxa], par[maxn], Max[maxn], val[maxn], f[maxn]; void extend(int x) { int p = last, np = ++ToT; Max[np] = Max[p] + 1; val[np] = 1; last = np; while(p && !to[p][x]) to[p][x] = np, p = par[p]; if(!p){ par[np] = rt; return ; } int q = to[p][x]; if(Max[q] == Max[p] + 1){ par[np] = q; return ; } int nq = ++ToT; Max[nq] = Max[p] + 1; if(!T) val[nq] = 1; memcpy(to[nq], to[q], sizeof(to[q])); par[nq] = par[q]; par[q] = par[np] = nq; while(p && to[p][x] == q) to[p][x] = nq, p = par[p]; return ; } int sa[maxn], Ws[maxn]; int main() { scanf("%s", S); T = read(); K = read(); len = strlen(S); rt = last = ToT = 1; for(int i = 0; i < len; i++) extend(S[i] - 'a'); for(int i = 1; i <= ToT; i++) Ws[len-Max[i]]++; for(int i = 1; i <= len; i++) Ws[i] += Ws[i-1]; for(int i = ToT; i; i--) sa[Ws[len-Max[i]]--] = i; if(T) for(int i = 1; i <= ToT; i++) { int u = sa[i]; if(par[u] != rt) val[par[u]] += val[u]; } for(int i = 1; i <= ToT; i++) { int u = sa[i]; f[u] = val[u]; for(int c = 0; c < maxa; c++) f[u] += f[to[u][c]]; } if(K > f[1]) return puts("-1"), 0; int p = rt; while(K > 0) { for(int c = 0; c < maxa; c++) if(K > f[to[p][c]]) K -= f[to[p][c]]; else { putchar(c + 'a'); K -= val[p = to[p][c]]; break; } } putchar('\n'); return 0; }