【洛谷P3804】统计每个子串出现的次数和长度(后缀自动机模版+拓扑序计数)

题目地址:https://www.luogu.org/problem/P3804

解题思路:


对于此题求拓扑序的两种方法:

(1)桶排序法:最终y[x]=i, 表示在拓扑序中第x个点是i

    void _count()
    {
        memset(x,0,sizeof(x));
        for(int i=1;i<=cnt;i++) x[len[i]]++;
        for(int i=1;i<=cnt;i++) x[i]+=x[i-1];/
        for(int i=1;i<=cnt;i++) y[x[len[i]]--]=i;
        for(int i=cnt;i>=1;i--)
            cnt[link[y[i]]]+=cnt[y[i]];
    }

(2)dfs

vector<int> v[MAXN];
LL ans = 0;
for(int i = 2; i <= tot; i++) v[link[i]].push_back(i);
void dfs(int x) {
    for(int i = 0; i < v[x].size(); i++)
        dfs(v[x][i]), cnt[x] += cnt[v[x][i]];
    if(siz[x] > 1) ans = max(ans, 1ll * cnt[x] * maxlen[x]);
}

例如字符串aabbabd,建立的SAM和拓扑序(红色箭头路线)为:

图源:https://www.cnblogs.com/zjp-shadow/p/9218214.html

 

ac代码:


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e6+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];
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]];
}
int main()
{
    scanf("%s", s);
    int len = strlen(s);
    init();
    for(int i = 0; i < len; i++) insert(s[i]-'a');
    count();
    ll ans = 0;
    for(int i = 1; i <= num; i++)
        if(cnt[i] > 1)
            ans = max(ans, 1ll*maxlen[i]*cnt[i]);
    printf("%lld\n", ans);
    return 0;
}

 

全部评论

相关推荐

今天周一休息,突发奇想写一篇阶段总结。如题,我已经去了一个和Java彻底毫无关联的行业。曾经我以为自己能在计算机行业发光发热,没想到刚入行一年多就当了逃兵。从最开始的热爱到现在一看到代码就厌恶,不知道自己经历了什么。所以我去干什么了?答案是:在成都当了租房销售。上班那会压力大了就念叨着去干租房中介,但是一直下不去这个决心,想着自己学了四年多的计算机知识,终究还是不甘心。终于在某一天准备八股文的时候,看着无数篇和工作内容关系不大的理论知识,那一刻下定决心,决定尝试一下销售行业,也算是给自己一个交代。后面阴差阳错的投了成都自如去当租房管家,没想到面试很顺利,在当天一百多个面试的人里面,我成为了为数不多通过的几个幸运儿之一。目前已经培训通过,正式入职,也开了单,也有压力但是每天过得很开心,真心喜欢那种和人交流的感觉,哪怕是最后没有选择找我租房。说这些也是想告诉那些大三,大四正在找Java实习而焦虑的同学:你们现在还年轻,选择很多,容错率也很高,可以尽情去尝试自己喜欢的行业和工作。不用因为某一次的面试没通过或者简历石沉大海而焦虑,更不用因为身边人都在挤编程的独木桥就强迫自己跟风。也算是自己的碎碎念吧,也希望自己能在新的领域取得一点小成就。也祝牛油工作顺利!
沉淀小子:干啥都不丢人啊,生存是必须要的,销售很考验一个人综合素质能力的,好的销售人脉和资源可不比写字楼的白领差啊
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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