HihoCoder1445 后缀自动机二·重复旋律5

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。

现在小Hi想知道一部作品中出现了多少不同的旋律?

输入

共一行,包含一个由小写字母构成的字符串。字符串长度不超过 1000000。

输出

一行一个整数,表示答案。

题解:

题目意思就是让你求有多少种不同的子串。

后缀自动机的板题。理解后缀自动机的longest[]数组,对于每一个状态s,它的等价类集合中的子串数量即为 l[s]-l[fa[s]];然后答案就是把每一个状态的的数量累加;

(原因就是任意两个等价类交集为空)

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
char s[maxn];
int len;
int T;
struct SAM{
    int last,cnt,nxt[maxn*2][26],fa[maxn*2],l[maxn*2];
    long long ans;
    void init()
    {
        last = cnt=1;
        memset(nxt[1],0,sizeof nxt[1]);
        fa[1]=0;ans=0;l[1]=0;
    }
    int inline newnode()
    {
        ++cnt;
        memset(nxt[cnt],0,sizeof nxt[cnt]);
        fa[cnt]=l[cnt]=0;
        return cnt;
    }
    void add(int c)
    {
        int p=last;
        int np=newnode();
        last=np;
        l[np]=l[p]+1;
        while(p&&!nxt[p][c])
        {
            nxt[p][c]=np;
            p = fa[p];
        }
        if(!p) fa[np]=1;
        else
        {
            int q = nxt[p][c];
            if(l[q]==l[p]+1) fa[np] = q;
            else
            {
                int nq = newnode();
                memcpy(nxt[nq],nxt[q],sizeof nxt[q]);
                fa[nq] = fa[q];
                l[nq] = l[p]+1;
                fa[np]=fa[q]=nq;
                while (nxt[p][c]==q)
                {
                    nxt[p][c]=nq;
                    p=fa[p];
                }
            }
        }
        ans+=(l[last]-l[fa[last]])*1ll;
    }
    void query()
    {
        int l=1,r=strlen(s+1);
        //scanf("%d%d",&l,&r);
        init();
        for(int i=l;i<=r;i++) add(s[i]-'a');
        printf("%lld\n",ans);
    }
}sam;
int main()
{
    scanf("%s",s+1);
    sam.query();
    return 0;
} 
View Code

 

posted @ 2019-05-03 14:00  StarHai  阅读(217)  评论(0编辑  收藏  举报