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\n",ans);
    } 
    return 0;
}
View Code

 

posted @ 2019-05-03 18:12  StarHai  阅读(299)  评论(0编辑  收藏  举报