【SPOJ-SUBLEX】Lexicographical Substring Search(字典序第k小的子串(不重复统计同一个子串)---后缀自动机)

题目地址:https://cn.vjudge.net/problem/SPOJ-SUBLEX

解题思路:


这道题用后缀数组也很好做,参见例题HDU5008,此处用SAM做。

建好SAM,根据得到的拓扑序,可以推得从每个状态出发的子串数目。

void getsum()//从某个状态点出发能有多少个子串
{
    //倒推,i是拓扑序的序号
    for(int i = num; i >= 1; i--)
    {
        sum[y[i]] = 1;//默认值为1,因为从自己不出发就能形成一个子串
        for(int j = 0; j <= 25; j++)
            sum[y[i]] += sum[trans[y[i]][j]];
    }
}

现在相当于得到一个这样的东西:

现在只需利用getsum函数得到的信息在这个上面寻找字典序第k小的子串了

 

ac代码:


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 9e4+100;
int maxlen[maxn<<1], trans[maxn<<1][30], link[maxn<<1];
int x[maxn<<1], y[maxn<<1], cnt[maxn<<1];
int num, last;//num表示状态数
char s[maxn];
int sum[maxn<<1];//与状态数有关,开两倍
void init()
{
    num = last = 1;//起始状态的编号为1
    maxlen[last] = link[last] = 0;//起始位置为空字符,长度为0
}
void insert(int id)
{
    int z = ++num, p;
    maxlen[z] = maxlen[last]+1;
    for(p = last; p && !trans[p][id]; p = link[p]) trans[p][id] = z;
    if(!p) link[z] = 1;//路径上不存在跳转
    else//连接路径上已存在跳转
    {
        int x = trans[p][id];//第一个存在跳转的状态跳转去的状态
        if(maxlen[x] == maxlen[p]+1) link[z] = x;//一个
        else//多个
        {
            int y = ++num;
            maxlen[y] = maxlen[p]+1;
            memcpy(trans[y], trans[x], sizeof(trans[x]));
            link[y] = link[x];
            for(; p && trans[p][id] == x; p = link[p]) trans[p][id] = y;
            link[x] = link[z] = y;
        }
    }
    last = z;
    cnt[z] = 1;
}

void count()
{
    memset(x, 0, sizeof x);
    for(int i = 1; i <= num; i++) x[maxlen[i]]++;
    for(int i = 1; i <= num; i++) x[i] += x[i-1];
    for(int i = 1; i <= num; i++) y[x[maxlen[i]]--] = i;
    for(int i = num; i >= 1; i--)
        cnt[link[y[i]]] += cnt[y[i]];//得到每个状态的字符串集出现的次数
}
void getsum()//从某个状态点出发能有多少个子串
{
    //倒推,i是拓扑序的序号
    for(int i = num; i >= 1; i--)
    {
        sum[y[i]] = 1;//默认值为1,因为从自己不出发就能形成一个子串
        for(int j = 0; j <= 25; j++)
            sum[y[i]] += sum[trans[y[i]][j]];
    }
}
void get_Ksub(int k)
{
    int cur = 1, nxt;
    string ans = "";
    while(k)
    {
        for(int i = 0; i <= 25; i++)
        {
            if(trans[cur][i] && k)
            {
                nxt = trans[cur][i];
                if(k > sum[nxt]) k -= sum[nxt];
                else
                {
                    k--;//字典序增大1
                    cur = nxt;
                    ans += char(i+'a');
                    break;
                }
            }
        }
    }
    cout << ans << endl;
}
int main()
{
    scanf("%s", s);
    int len = strlen(s), q, k;
    init();
    for(int i = 0; i < len; i++) insert(s[i]-'a');
    count();
    getsum();
    scanf("%d", &q);
    while(q--)
    {
        scanf("%d", &k);
        get_Ksub(k);
    }
    return 0;
}

 

全部评论

相关推荐

刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结:&nbsp;27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
字节7000实习来了,你...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务