[模板]后缀自动机

Posted papercloud

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模板]后缀自动机相关的知识,希望对你有一定的参考价值。

传送门

Description

给定一个只包含小写字母的字符串(S),

请你求出 (S) 的所有出现次数不为 (1) 的子串的出现次数乘上该子串长度的最大值。

Solution

保持好习惯吧,模板题还是放一下

SAM的板子,想必是到处都有,反正都比我写的好看。。。

当初想学SAM的时候,就被某俄文翻译的(20000)字论文吓跑了。。。

核心?

  • 作为一种可以表示所有后缀的状态的自动机,它得满足状态数尽可能的小
  • SAM的做法:
    1. 每个状态表示所有(Right)集合相同的子串,这里(Right)集合的定义可是一个子串在原串中所有出现位置的右端点的集合。
    2. 对于每个状态,我们定义一个(step),表示该状态所能表示的所有子串中长度最大值
    3. (fa)指针,满足当前串的(Right)集合是(fa)指向的状态的真子集,且是最大的那一个,可以发现,(fa)指针所指向的状态一定是当前状态子串的一个后缀。
    4. 在线加点,每次加点后最多只会增加两个新的状态


Code?

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
class Suf_Automation
{
    #define MX 2000005
    private:
        int c[MX][26],fa[MX],step[MX],v[MX],rk[MX],val[MX];
        int last,cnt,n;
        ll ans=0;
    public:
        inline void init(int len)
        {
            cnt=last=1;n=len;
            for(int i=1;i<=n<<1;++i)
            memset(c[i],0,sizeof c[i]),step[i]=fa[i]=v[i]=val[i]=0;
        }
        void Insert(int x)
        {
            int p=last,np=++cnt;step[np]=step[p]+1;val[np]=1;
            for(;p&&!c[p][x];p=fa[p]) c[p][x]=np;
            if(!p) fa[np]=1;
            else 
            {
                int q=c[p][x];
                if(step[q]==step[p]+1) fa[np]=q;
                else 
                {
                    int nq=++cnt;step[nq]=step[p]+1;
                    memcpy(c[nq],c[q],sizeof c[q]);
                    fa[nq]=fa[q];fa[np]=fa[q]=nq;
                    for(;c[p][x]==q;p=fa[p]) c[p][x]=nq;
                }    
            }
            last=np;
        }
        inline void Query()
        {
            register int i;
            for(i=1;i<=cnt;++i) ++v[step[i]];
            for(i=1;i<=n;++i) v[i]+=v[i-1];
            for(i=1;i<=cnt;++i) rk[v[step[i]]--]=i;
            for(i=cnt;i;--i)
            {
                val[fa[rk[i]]]+=val[rk[i]];
                if(val[rk[i]]>1) ans=max(ans,1ll*val[rk[i]]*step[rk[i]]);
            }
            val[1]=0;
            printf("%lld
",ans);
        }
    #undef MX
}pac;
#define MN 1000005
char s[MN];
int main()
{
    scanf("%s",s+1);
    register int i,n=strlen(s+1);
    pac.init(n);
    for(i=1;i<=n;++i) pac.Insert(s[i]-'a');
    pac.Query();
    return 0;
}


SAM的作用?

很好的维护子串信息的工具嘛。

下面附上一个简单的,查询某个串的出现次数?

int Calc(char*s,int l,int r)
{
    int x=1;
    for(int i=l;i<=r;++i)
        if(!c[x][s[i]-'a']) return 0;
        else x=c[x][s[i]-'a'];    
    return val[x];
}

可以发现,顺着SAM查询子串,到达的状态是所有与查询的子串出现情况相类似(出现次数肯定是相同的)的子串集合,当然,待查子串也在这个集合里辣。



Blog来自PaperCloud,未经允许,请勿转载,TKS!

以上是关于[模板]后缀自动机的主要内容,如果未能解决你的问题,请参考以下文章

后缀自动机 模板题

hdu4622(后缀自动机模板)

P3804 模板后缀自动机

vscode 用户代码片段 vue初始化模板 Snippet #新加入开头注释 自动生成文件名 开发日期时间等内容

[P6139] 模板广义后缀自动机 - 广义SAM

Luogu3804模板后缀自动机(后缀自动机)