[SPOJ8222]NSUBSTR(后缀自动机)

本文介绍了一种利用自动机求解不同长度子串最大出现次数的方法。通过构建自动机并运用Right集合特性,文章详细解释了如何计算每个节点对应的Right集合大小,并将这些信息映射到特定长度的子串上。

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

题目:

我是超链接

题意:

f(i)表示的是长度为i的子串的最大出现次数,求f(1..n)

题解:

这类问题大多数是利用Right集合的性质,因为一个节点代表的字符串一定在Right集合包含的所有位置上重复出现。想要得到关于每个节点Right集合的信息,只需要在建立自动机以后按照拓扑序从后往前,用每个节点来更新它的fa节点就可以了。

对于这道题目来说,它要求每个长度的串出现次数的最大值,那么只需要对于每个节点递推出它的Right集合大小,然后映射到长度上面去就可以了。需要注意的一个问题就是如果长度为i的串出现了k次,那么长度为i-1的串一定至少出现了k次,因为可以取那些串的后缀。所以最后还要用F(i)去更新一下F(i-1)。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=500005;
int ch[N*2][30],step[N],last,q,nq,p,np,cnt,fa[N],r[N],c[N],a[N],f[N];
char st[N];
void insert(int c)
{
    p=last; last=np=++cnt;
    step[np]=step[p]+1;
    while (p && !ch[p][c]) ch[p][c]=np,p=fa[p];
    if (!p) {fa[np]=1;return;}
    q=ch[p][c];
    if (step[q]==step[p]+1){fa[np]=q;return;}
    nq=++cnt; step[nq]=step[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",st+1);
    int l=strlen(st+1);
    last=cnt=1;
    for (int i=1;i<=l;i++) insert(st[i]-'a');

    for (int i=1;i<=cnt;i++) c[step[i]]++;
    for (int i=1;i<=cnt;i++) c[i]+=c[i-1];
    for (int i=1;i<=cnt;i++) a[c[step[i]]--]=i;
    p=1;
    for (int i=1;i<=l;i++) p=ch[p][st[i]-'a'],r[p]++;
    for (int i=cnt;i;i--)
    {
        int t=a[i];
        r[fa[t]]+=r[t];
        f[step[t]]=max(f[step[t]],r[t]);
    }
    for (int i=l-1;i;i--) f[i]=max(f[i],f[i+1]);
    for (int i=1;i<=l;i++) printf("%d\n",f[i]); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值