spoj8222:Substrings 后缀自动机+DP

本文深入探讨了SAM算法的入门级概念,包括构造SAM、理解righti和leni及其贡献、求解righti的方法以及最终的递推更新步骤。通过实例代码解析,清晰展示了算法的核心思想及实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看后缀自动机看的真是生不如死,人太弱表示看不懂,现在还是没有完全理解啊。
这是一道SAM的入门题。
我们首先构造出SAM,然后SAM中每个节点i有一个rightileni,那么长度在(lenfai,leni]内的都出现了righti次。考虑如果一个长度为leni的出现了x次,那么长度<leni的一定也至少出现了x次,所以我们可以直接算每个rightileni的贡献。最后递推更新一下答案令fi=max(fi,fi+1)就好了。
现在还有一个问题没解决,就是righti的求法,我们只需要righti的大小而不需要集合内究竟是哪些数,所以可以开始给每个代表s[1..i]的节点irighti=1,然后按深度合并一下,就能O(|S|)内求出所有的right了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
using namespace std;
char s[N];
int S,n,cnt,last;
int a[N],f[N],t[N],fa[N],cc[N],len[N],r[N],ch[N][26];
inline void insert(int x)
{
    int c=a[x];
    int p=last,np=++cnt; last=np;
    len[np]=x;
    while (p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
    if (!p) fa[np]=S;
    else
    {
        int q=ch[p][c];
        if (len[p]+1==len[q]) fa[np]=q;
        else
        {
            int nq=++cnt;
            len[nq]=len[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q]; fa[np]=fa[q]=nq;
            while (ch[p][c]==q) ch[p][c]=nq,p=fa[p];
        }
    }
}
int main()
{
    scanf("%s",s+1);
    last=S=++cnt;
    n=strlen(s+1);
    for (int i=1;i<=n;i++)
        a[i]=s[i]-'a';
    for (int i=1;i<=n;i++)
        insert(i);
    for (int i=1,p=S;i<=n;i++)
        p=ch[p][a[i]],r[p]++;
    for (int i=1;i<=cnt;i++) ++cc[len[i]];
    for (int i=1;i<=n;i++) cc[i]+=cc[i-1];
    for (int i=1;i<=cnt;i++) t[cc[len[i]]--]=i;
    for (int i=cnt;i;i--) r[fa[t[i]]]+=r[t[i]];
    for (int i=1;i<=cnt;i++) f[len[i]]=max(f[len[i]],r[i]);
    for (int i=n;i;i--) f[i]=max(f[i],f[i+1]);
    for (int i=1;i<=n;i++) printf("%d\n",f[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值