• HihoCoder1445 重复旋律5(后缀自动机)


    重复旋律5

    时间限制:10000ms
    单点时限:2000ms
    内存限制:512MB

    描述

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

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

    输入

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

    输出

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

    样例输入
    aab
    样例输出
    5

    2017-11-22:从代码看,是比ac自动机优美,但是ac自动机是基础在kmp和字典树上面,学习起来很快,然而后缀自动机就灰常难以YY啦。

    目前感觉似懂非懂,等把后面几个题AC了再回来整理吧。

     

    2017-11-25:三天,做了几个题之后,基本上是弄懂了。感觉后缀自动机很强大,打算再花一周来练习。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    using namespace std;
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const int N=1000010;
    int tot=1,n,slink[2*N],trans[2*N][26],minlen[2*N],maxlen[2*N];
    char str[N];
    ll ans=0;
    int newstate(int _maxlen,int _minlen,int* _trans,int _slink) {
        maxlen[++tot]=_maxlen;
        minlen[tot]=_minlen;
        slink[tot]=_slink;
        if(_trans)
            for(int i=0; i<26; i++)
                trans[tot][i]=_trans[i];
        else for(int i=0;i<26;i++)
                trans[tot][i]=-1;
        return tot;
    }
    int add_char(char ch,int u) {
        int c=ch-'a',v=u;
        int z=newstate(maxlen[u]+1,-1,NULL,0);
        while(v!=-1&&trans[v][c]==-1) {
            trans[v][c]=z;
            v=slink[v];
        }
        if(v==-1) {
            minlen[z]=1;
            slink[z]=0;
            return z;
        } 
        int x=trans[v][c];
        if(maxlen[v]+1==maxlen[x]) {
            slink[z]=x;
            minlen[z]=maxlen[x]+1;
            return z;
        }
        int y=newstate(maxlen[v]+1,-1,trans[x],slink[x]);
        slink[z]=slink[x]=y;
        minlen[x]=minlen[z]=maxlen[y]+1;
        while(v!=-1&&trans[v][c]==x) {
            trans[v][c]=y;
            v=slink[v];
        }
        minlen[y]=maxlen[slink[y]]+1;
        return z;
    }
    int main() {
        scanf("%s",str);
        int len=strlen(str),pre=0;
        memset(trans[0],-1,sizeof(trans[0]));
        slink[0]=-1;
        for(int i=0; i<len; i++) {
            pre=add_char(str[i],pre);
        }
        for(int i=2; i<=tot; i++) {
            ans+=maxlen[i]-minlen[i]+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }

    做了HDU4641再回来改动一下,minlen[i]=maxlen[slink[i]],所以没必要保存minlen了。代码稍微简短了一点:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    using namespace std;
    const int maxn=1000100;
    int tot,slink[2*maxn],trans[2*maxn][26],maxlen[2*maxn];
    char str[2*maxn];
    int N,M,K,num[2*maxn],last;
    long long ans;
    void init()
    {
        ans=tot=0;
        last=0;
        memset(trans[0],-1,sizeof(trans[0]));
        slink[0]=-1; maxlen[0]=0;
    }
    void add_char(char chr) 
    {
        int c=chr-'a';
        int p=last,np=++tot;
        maxlen[np]=maxlen[p]+1;
        memset(trans[np],-1,sizeof(trans[np])); 
        num[np]=0;
        while(p!=-1&&trans[p][c]==-1)  trans[p][c]=np,p=slink[p];
        if(p==-1) slink[np]=0;
        else
        {
            int q=trans[p][c];
            if(maxlen[q]!=maxlen[p]+1)
            {
                int nq=++tot;
                memcpy(trans[nq],trans[q],sizeof(trans[q])); num[nq]=num[q];
                maxlen[nq]=maxlen[p]+1;
                slink[nq]=slink[q];
                slink[np]=slink[q]=nq;
                while(p!=-1&&trans[p][c]==q) trans[p][c]=nq,p=slink[p];
            }
            else slink[np]=q;
        }
        last=np;
    }
    int main() {
        init();
        scanf("%s",str);
        N=strlen(str);
        for(int i=0; i<N; i++)   add_char(str[i]);
        for(int i=1;i<=tot;i++) ans+=maxlen[i]-maxlen[slink[i]];
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    Go对比其他语言新特性2(函数、包、错误处理)
    计算机基础知识
    GO的下载和环境配置,Goland编译器的配置和初始化项目
    软件工程第五次作业
    软件工程第四次作业
    软件工程第三次作业
    软件工程第二次作业
    软件工程第一次作业
    软件工程结对第二次作业
    软件工程结对第一次作业
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7880624.html
Copyright © 2020-2023  润新知