bzoj3998: [TJOI2015]弦论

14 篇文章 0 订阅

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

其实就是SAM裸题吧..

#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <algorithm> 
using namespace std; 
const int Maxn = 500010; 
int F[Maxn*2], d[Maxn*2], ch[Maxn*2][26], f[Maxn*2], val[Maxn*2], tot, now; 
int Rsort[Maxn*2], rk[Maxn*2]; 
char s[Maxn]; int len; 
int t, K; 
int copy ( int p, int c ){ 
    int x = ++tot, y = ch[p][c]; 
    d[x] = d[p]+1; 
    for ( int i = 0; i < 26; i ++ ) ch[x][i] = ch[y][i]; 
    F[x] = F[y]; F[y] = x; 
    while ( ~p && ch[p][c] == y ){ ch[p][c] = x; p = F[p]; } 
    return x; 
} 
void add ( int c ){ 
    int p, o; 
    if ( p = ch[now][c] ){ 
        if ( d[p] != d[now]+1 ) copy ( now, c ); 
        now = ch[now][c]; 
    } 
    else { 
        d[o=++tot] = d[now]+1; p = now; now = o; f[o] = 1; 
        while ( ~p && !ch[p][c] ){ ch[p][c] = o; p = F[p]; } 
        F[o] = ~p ? ( d[p]+1 == d[ch[p][c]] ? ch[p][c] : copy ( p, c ) ) : 0; 
    } 
} 
int main (){ 
    int i, j, k; 
    scanf ( "%s", s+1 ); len = strlen (s+1); 
    F[0] = -1; 
    scanf ( "%d%d", &t, &K ); 
    for ( i = 1; i <= len; i ++ ) add (s[i]-'a'); 
    for ( i = 1; i <= tot; i ++ ) Rsort[d[i]] ++; 
    for ( i = 1; i <= len; i ++ ) Rsort[i] += Rsort[i-1]; 
    for ( i = tot; i >= 1; i -- ) rk[Rsort[d[i]]--] = i; 
    for ( i = tot; i >= 1; i -- ){ 
        if ( t == 1 ) f[F[rk[i]]] += f[rk[i]]; 
        else f[rk[i]] = 1; 
        val[rk[i]] = f[rk[i]]; 
    } 
    for ( i = tot; i >= 1; i -- ){ 
        for ( j = 0; j < 26; j ++ ) if ( ch[rk[i]][j] > 0 ) f[rk[i]] += f[ch[rk[i]][j]]; 
    } 
    for ( i = 0; i < 26; i ++ ){ 
        if ( ch[0][i] ){ 
            f[0] += f[ch[0][i]]; 
        } 
    } 
    if ( K > f[0] ){ printf ( "-1\n" ); return 0; } 
    now = 0; 
    while ( K > 0 ){ 
        K -= val[now]; 
        if ( K <= 0 ) break; 
        for ( i = 0; i < 26; i ++ ){ 
            if ( ch[now][i] ){ 
                if ( K <= f[ch[now][i]] ){ 
                    printf ( "%c", i+'a' ); 
                    now = ch[now][i]; 
                    break; 
                } 
                else K -= f[ch[now][i]]; 
            } 
        } 
    } 
    printf ( "\n" ); 
    return 0; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值