Live2d Test Env

HihoCoder1465 重复旋律8(后缀自动机)

描述

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

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

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

输入

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

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

输出

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

样例输入
abac
3
a
ab
ca
样例输出
2
2
1
 【技巧】:

现在我们要处理T的循环同构串们。这里有一个常用的技巧,假设T的长度是n,我们令T'=T + T[1..n-1]形成一个新的串T'。例如对于"abcd",我们把"abc"拼在"abcd"后面,得到新的T="abcdabc"。这样"abcd"的循环同构串就变成了T'="abcdabc"的长度为n的子串。

小Ho:哦!然后我们再用之前讲的方法求出在每个位置T'[i]结束的最长公共子串。我们可以求出对应的(u, l),如果这时l>=n,那我们就得到了一个公共子串T'[i-l+1 .. i]。这个子串在S中出现的次数是|endpos(u)|,又恰好包含T的循环同构串T'[i-n+1 .. i]。

小Hi:基本思路是对的。但是要注意处理两个特殊情况。第一个情况是T的n个循环同构子串有重复(相同)的情况。比如T="aa",T'="aaa",还是以S="aabbabd"为例

S:  aabbabd
T': aaa
1: a       (u, l) = (1, 1)
2:  aa      (u, l) = (2, 2), l>=n
3:   aa     (u, l) = (2, 2), l>=n

小Hi:T'[2]和T'[3]结尾的最长公共子串都是"aa",(u, l)都是(2, 2)。我们要避免"aa"的出现次数被统计2次,小Ho你想想要怎么办?

小Ho:恩,我们要记录一个状态是不是之前在l>=n的情况下到达过。如果到达过的话,下一次再到达就不要统计了。

小Hi:很好。我们还有第二个特殊情况要处理。那就是要区分串T'[i-l+1 .. i]出现次数和T'[i-n+1 .. i]的出现次数。前面说到,我们处理T'[i]的时候求出当前状态u和匹配长度l。这时串T'[i-l+1 .. i]一定是属于状态u的,T'[i-l+1 .. i]的出现次数是|endpos(u)|。但是这时可能l>n,所以T'[i-n+1 .. i]不一定属于状态u。T'[i-n+1 .. i]是T'[i-l+1 .. i]长度为n的后缀,可能在suffix-path(u->S)上,出现次数比T'[i-n+1 .. i]多。

小Ho:这个也好办,我们只要沿着suffix-path(u->S)向上找,找到最靠近S的v满足maxlen[v]>=n (也就是minlen[v]<=n<=maxlen[v]),统计|endpos(v)|即可。

小Hi:这里有一个关键点,我们找到v之后可以直接令u=v。以免每次向前找v的复杂度过高。

 

此题感悟:把trans当成KMP的fail函数,从而后缀自动机可以实现KMP和ac自动机的大部分功能。

注意题中的S=1;

此外字符串处理后再用strlen会出错?好像是。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
const int N=1e6+100;
int q[N*2],tail,head;
int tot,slink[2*N],trans[2*N][26],minlen[2*N],maxlen[2*N],edpts[2*N];
int blue[2*N],ind[2*N],used[2*N];
char str[2*N];
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];
    return tot;
}
int add_char(char ch,int u) {
    int c=ch-'a',v=u;
    int z=newstate(maxlen[u]+1,-1,NULL,0);
    blue[z]=1;//绿色 
    while(v&&!trans[v][c]) {
        trans[v][c]=z;
        v=slink[v];
    }
    if(!v) {
        minlen[z]=1;
        slink[z]=1;
        ind[0]++;
        return z;
    }
    int x=trans[v][c];
    if(maxlen[v]+1==maxlen[x]) {
        slink[z]=x;
        minlen[z]=maxlen[x]+1;
        ind[x]++;
        return z;
    }
    int y=newstate(maxlen[v]+1,-1,trans[x],slink[x]);
    slink[z]=slink[x]=y;
    ind[y]+=2;
    minlen[x]=minlen[z]=maxlen[y]+1;
    while(v&&trans[v][c]==x) {
        trans[v][c]=y;
        v=slink[v];
    }
    minlen[y]=maxlen[slink[y]]+1;
    return z;
}
void top_sort() {
    head=tail=0;
    for(int i=1;i<=tot;i++)if(!ind[i]) q[++tail]=i;
    while(head<tail) {
        int u=q[++head];
        if(blue[u]) edpts[u]++;
        edpts[slink[u]] += edpts[u];
        if(!--ind[slink[u]]) q[++tail]=slink[u];
    }
}
void _count()
{
    char c[2*N];
    scanf("%s",c);
    int len,L0,i,u=1,ans=0,L=0;//1
    L0=strlen(c);
    for(i=0;i<L0-1;i++) c[i+L0]=c[i];
    len=2*L0-1;//改成strlen就错了!!! 
    for(i=0;i<=tot;i++) used[i]=0;
    for(i=0;i<len;i++){
        while(u!=1&&trans[u][c[i]-'a']==0) {
            u=slink[u];
            L=maxlen[u];
        }
        if(trans[u][c[i]-'a']>0) {
            u=trans[u][c[i]-'a'];
            L++;
        }
        else { 
            u=1;
            L=0;
        }//2
        if(L>L0){
            while(maxlen[slink[u]]>=L0){
                u=slink[u];
                L=maxlen[u];
            }        
        }
        if(L>=L0&&!used[u]) {
            ans+=edpts[u];
            used[u]=1;
        }
    }
    printf("%d\n",ans);
}
int main() {
    scanf("%s",str);
    int len=strlen(str),pre=1;
    tot=1;
    for(int i=0; i<len; i++) {
        pre=add_char(str[i],pre);
    }
    top_sort();
    int T;
    scanf("%d",&T);
    while(T--)   _count();
    return 0;
} 

 

posted @ 2017-11-24 22:20  nimphy  阅读(222)  评论(0编辑  收藏  举报