[BZOJ]3998 [TJOI2015] 弦论 后缀自动机

来源:互联网 发布:软件机器码修改 编辑:程序博客网 时间:2024/05/16 07:57

3998: [TJOI2015]弦论

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 3292  Solved: 1133
[Submit][Status][Discuss]

Description

对于一个给定长度为N的字符串,求它的第K小子串是什么。

Input

 第一行是一个仅由小写英文字母构成的字符串S

第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

Output

输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1

Sample Input

aabc
0 3

Sample Output

aab

HINT

 N<=5*10^5


T<2

K<=10^9

Source

[Submit][Status][Discuss]


HOME Back

  以前wjj给我讲过但那是暑假的事了... 现在几乎都忘光了... 不过重拾起来还是非常快? 虽然一开始看毛子论文的各种专业术语乱飚一脸懵逼, 不过努力回想wjj讲过的内容在纸上推推还是推出来了... 明白了以后看了下clj神犇的论文发现这玩意性质还非常多? 感觉比今早上学的回文自动机要高级多了... 看来还是得多学习一个... 争取深入理解后缀自动机, 把性质都记录下来推一推多做点题.

  这道题很简单, 两种情况分类讨论逆拓扑序倒推预处理, 然后询问第k大可以说是...26分?

#include<stdio.h>#include<cstring>#include<algorithm>using namespace std;const int maxn = 1000005;char ss[maxn];int n, k, T, cnt = 1, last = 1;int ws[maxn], sa[maxn], c[maxn][26], dep[maxn], val[maxn], sum[maxn], par[maxn];struct sam{inline void build(int x) {int p = last, np = last = ++ cnt;dep[np] = dep[p] + 1; val[np] = 1;while (p && !c[p][x]) c[p][x] = np, p = par[p];if (!p) par[np] = 1;else {int who = c[p][x];if (dep[who] == dep[p] + 1) par[np] = who;else {int anwho = ++ cnt;dep[anwho] = dep[p] + 1;memcpy(c[anwho], c[who], sizeof(c[who]));par[anwho] = par[who];  par[np] = par[who] = anwho;  while (c[p][x] == who) c[p][x] = anwho, p = par[p];  }}}inline void init() {for (int i = 1; i <= cnt; ++ i) ws[dep[i]] ++;  for (int i = 1; i <= n; ++ i) ws[i] += ws[i - 1];for (int i = cnt; i; -- i) sa[ws[dep[i]]--] = i;for (int i = cnt; i; -- i) {int t = sa[i];if (T & 1) val[par[t]] += val[t];else val[t] = 1;}val[1] = 0;for (int i = cnt; i; -- i) {int t = sa[i]; sum[t] = val[t];for (int ch = 0; ch < 26; ++ ch)  sum[t] += sum[c[t][ch]];}}void calc(int x,int k) {  while(true) {if (k <= val[x]) return;k -= val[x];for (int i = 0; i < 26; ++ i) {  int t = c[x][i];  if (t) {  if (k <= sum[t]) {  putchar(i + 'a');  x = t;  break;}k -= sum[t];}}}    }}sam;  int main() {scanf("%s", ss + 1); n = strlen(ss + 1);scanf("%d%d", &T, &k);for (int i = 1; i <= n; ++ i) sam.build(ss[i] - 'a');sam.init();if (k > sum[1]) puts("-1");else sam.calc(1,k);return 0;}


原创粉丝点击