P3975 [TJOI2015]弦论

题目描述

为了提高智商,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;
}
上一篇:洛谷 P3806 【模板】点分治1


下一篇:[JSOI2008]火星人 - Splay + 哈希