hihoCoder #1465 : 后缀自动机五·重复旋律8

http://hihocoder.com/problemset/problem/1465

 

求S的循环同构串在T中的出现次数

将串S变成SS

枚举SS的每个位置i,求出以i结尾的SS的子串 与 T的最长公共子串 

若长度>=|S|,说明以i结尾的S的循环同构串在T中出现过

假设最后匹配i到达了后缀自动机的a节点

沿着a的parent树以直向上走,走到离根最近的 匹配长度>=|S|的节点b

b的在parent树中的子树 叶子节点个数 即为这个以i结尾的循环同构串在T中的出现次数

 

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

#define N 1000001

char s[N<<1];

int ch[N<<1][26],tot=1;
int last=1,p,q,np,nq;
int fa[N<<1],len[N<<1];
int r[N<<1]; 

int v[N<<1];
int sa[N<<1];

int use[N<<1];

int now,now_len;
long long ans;

void extend(int c)
{
    len[np=++tot]=len[last]+1;
    for(p=last;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    if(!p) fa[np]=1;
    else
    {
        q=ch[p][c];
        if(len[q]==len[p]+1) fa[np]=q;
        else
        {
            nq=++tot;
            fa[nq]=fa[q];
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[q]=fa[np]=nq;
            len[nq]=len[p]+1;
            for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
        }
    }
    last=np;
}

void build()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;++i) 
    {
        r[tot+1]=1;
        extend(s[i]-'a');
    }
    for(int i=1;i<=tot;++i) v[len[i]]++;
    for(int i=1;i<=n;++i) v[i]+=v[i-1];
    for(int i=1;i<=tot;++i) sa[v[len[i]]--]=i;
    for(int i=tot;i;--i) r[fa[sa[i]]]+=r[sa[i]];
}

void find(int c,int n,int tim)
{
    while(now && !ch[now][c]) now=fa[now],now_len=len[now];
    if(!now)
    {
        now=1;
        now_len=0;
    }
    else now=ch[now][c],now_len++;
    if(now_len>n)
    while(len[fa[now]]>=n) 
    {
        now=fa[now];
        now_len=len[now];
    }
    if(now_len>=n && use[now]!=tim)
    {
        use[now]=tim;
        ans+=r[now];
    }
//    printf("%d\n",ans);
}
     
void solve()
{
    int n,m,L;
    scanf("%d",&n);
    for(int t=1;t<=n;++t) 
    {
        scanf("%s",s+1);
        m=strlen(s+1);
        for(int i=1;i<m;++i) s[m+i]=s[i];
        L=2*m-1;
        now=1; now_len=0;
        ans=0;
        for(int i=1;i<=L;++i) find(s[i]-'a',m,t);
        cout<<ans<<'\n';
    }
}

int main()
{
    freopen("rotate.in","r",stdin);
    freopen("rotate.out","w",stdout);
    build();
    solve();
}
    

 

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

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

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

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

解题方法提示

输入

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

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

输出

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

样例输入
abac
3
a
ab
ca
样例输出
2
2
1
posted @ 2018-05-04 08:44  TRTTG  阅读(449)  评论(0编辑  收藏  举报