zoukankan      html  css  js  c++  java
  • 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
    ",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;
    } 
  • 相关阅读:
    大话设计模式(二)代理模式 工厂方法模式 原型模式 模板方法模式 迪米特法模式 外观模式
    大话设计模式(一)简单工厂模式 策略模式 单一职责原则 开放-封闭原则 依赖倒置原则 装饰模式
    eclipse为hibernate.cfg.xml添加自动提示【转】
    Caused by: java.lang.NoSuchMethodError: javax.persistence.spi.PersistenceUnitInfo.getValidationMode
    Chrome 浏览器提示adobe flash player不是最新版本
    spring mvc <mvc:default-servlet-handler /> 。
    S2SH CRUD 整合
    Ms sql行转列。汇总
    Hibernate中的数据库方言(Dialect)
    SSH整合(1)异常
  • 原文地址:https://www.cnblogs.com/hua-dong/p/7892591.html
Copyright © 2011-2022 走看看