• HihoCoder1465 后缀自动机五·重复旋律8


    描述

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

    小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。

    小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。

    输入

    第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。

    第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。

    输出

    输出共N行,每行一个整数,表示答案。

    题解:

    复制模式串,以每一个字符作为后缀去匹配文本串,如果匹配长度>=len的话,进行累加;

    可能会出现重复匹配的情况,应该打上标记

    参考代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int INF=0x3f3f3f3f;
    const int maxn=2e6+10;
    char s[maxn],str[maxn];
    int m,mlen,now,use[maxn];
    ll ans;
    
    namespace SAM
    {
        int last,cnt,fa[maxn<<1],l[maxn<<1],nxt[maxn<<1][26];
        int c[maxn<<1],rk[maxn<<1],sum[maxn<<1];
        
        void Init()
        {
            last=cnt=1;
            memset(sum,0,sizeof(sum));
            memset(use,0,sizeof(use));
            memset(nxt[1],0,sizeof(nxt[1]));
            fa[1]=l[1]=0;
        } 
        
        int NewNode()
        {
            cnt++;
            memset(nxt[cnt],0,sizeof(nxt[cnt]));
            fa[cnt]=l[cnt]=0;
            return cnt;
        }
        
        void Add(int ch)
        {
            int p=last,np=NewNode();
            last=np; l[np]=l[p]+1;
            while(p&&!nxt[p][ch]) nxt[p][ch]=np,p=fa[p];
            if(!p) fa[np]=1;
            else
            {
                int q=nxt[p][ch];
                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][ch]==q) nxt[p][ch]=nq,p=fa[p];
                }
            }
        }
        
        void Build()
        {
            for(int i=1,len=strlen(s+1);i<=len;++i) sum[cnt+1]=1,Add(s[i]-'a');
            for(int i=1;i<=cnt;++i) c[l[i]]++;
            for(int i=1;i<=cnt;++i) c[i]+=c[i-1];
            for(int i=1;i<=cnt;++i) rk[c[l[i]]--]=i;
            for(int i=cnt;i;--i) sum[fa[rk[i]]]+=sum[rk[i]];
        }
        void Find(int c,int n,int ti)
        {
            while(now&&!nxt[now][c]) now=fa[now],mlen=l[now];
            if(!now) now=1,mlen=0;
            else now=nxt[now][c],mlen++;
            if(mlen>n)
            {
                while(l[fa[now]]>=n)
                {
                    now=fa[now];
                    mlen=l[now];
                }
            }
            if(mlen>=n&&use[now]!=ti)
            {
                use[now]=ti;
                ans+=1ll*sum[now];
            }
        }
    }
    using namespace SAM;
    
    int main()
    {
        scanf("%s%d",s+1,&m);
        Init();Build();
        for(int t=1;t<=m;++t)
        {
            scanf("%s",str+1);
            int n=strlen(str+1);
            for(int i=1;i<n;++i) str[n+i]=str[i];
            int l=2*n-1;
            now=1;ans=0;mlen=0;
            for(int i=1;i<=l;++i) Find(str[i]-'a',n,t);
            printf("%lld
    ",ans);
        } 
        return 0;
    }
    View Code
  • 相关阅读:
    Yantai
    Star War
    douban's woshang
    cannot change font's type in coreldraw
    LuXun said
    WaiTan
    free ubuntu disk前天就收到了寄来的光盘
    Winter Swimming
    before buy laptop买本本前,先来看看
    ubuntu beginer
  • 原文地址:https://www.cnblogs.com/csushl/p/10805922.html
Copyright © 2020-2023  润新知