zoukankan      html  css  js  c++  java
  • 可重叠最长重复子串

    Zvonko收到一条信息,是一个长长的字符串。抛开信息传递的内容,Zvonko发现这个字符串的某些子串,出现了不止一次。他写下所有的子串,想要知道,在字符串中出现至少两次的所有子串中,长度最长的为多少。
    就请你写一个程序帮助他吧!

    Input
    输入数据第一行包含一个整数L(1≤L≤200000),为给出的原串的长度。
    第二行包含一个仅由小写字符组成的,长度为L的字符串。
    Output
    输出最长的重复出现的字串的长度。如果这个串不存在,则输出0。

    Sample Input
    11
    sabcabcfabc
    Sample Output
    3

    Sol:

    对于字符串ababc,先做后缀排序得到
    ababc
    abc
    babc
    bc
    c


    然后对于ababc,从第一个字符开始取其直到结尾所形成的
    字符串,即第一个是ababc,然后到上面排序好的那里,求
    ababc与它前面那个字符的最长公共前缀,
    当然由于ababc是第一个,所以它前面一个不存在,LCP自然为0.
    然后拿出babc,求它与abc的LCP,其值为0
    再拿出abc,求它与ababc的LCP,其值为3
    再拿出bc,求它与babc的LCP,其值为1
    再拿出c,求它与bc的LCP,其值为0
    以上这些是我们手动可以算出来的。
    为什么要这样做呢,我们注意看第三步求abc,与ababc的Lcp=3.
    因为前二步得出的Lcp=0,是没有什么信息作用的。
    在第四步求bc时,因为Lcp(abc,ababc)=2,现在把它们首字母去掉后
    得到bc,babc.因为ababc排在abc前面,去掉首字母后babc也当然排在bc前面。
    而在求Lcp(bc,babc)时,我们可以知道这两个字母必然有2-1=1个字母是相同的。
    这样就充分利用了前面得到的信息,只要从bc,babc的第二个字母开始比起。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 200010
    int sa[N],Rank[N],buck[N],se[N],a[N],n,m,h[N],t; 
    char s[N];
    void bsort()
    {
        for(int i=1;i<=m;i++)buck[i]=0;
        for(int i=1;i<=n;i++)++buck[Rank[se[i]]];
        for(int i=1;i<=m;i++)buck[i]+=buck[i-1];
        for(int i=n;i>=1;i--)sa[buck[Rank[se[i]]]--]=se[i];
    }
    void get_sa()
    {
        for(int i=1;i<=n;i++)Rank[i]=a[i],se[i]=i;bsort();
        for(int i=1;i<=n;i<<=1)
        {
            int num=0;
            for(int j=n-i+1;j<=n;j++)
    		    se[++num]=j;
            for(int j=1;j<=n;j++)
    		    if(sa[j]>i)
    	        	se[++num]=sa[j]-i;
            bsort();
    		swap(se,Rank);
    		Rank[sa[1]]=num=1;
            for(int j=2;j<=n;j++)
                Rank[sa[j]]=(se[sa[j]]==se[sa[j-1]]&&se[sa[j]+i]==se[sa[j-1]+i])?num:++num;
            if(num==n)break;
            m=num;
        }
    }
    void get_lcp()
    {
        int len=0;
        for(int i=1;i<=n;i++) //sa数组与Rank互逆 
    	   Rank[sa[i]]=i;
        for(int i=1;i<=n;i++)
        //按位置顺序,取出每个从i开始的后缀A 
        {
            if(len)  //如果len大于0则减去1,因为相关的两个字符串去掉了首字母 
    		   --len;
            int j=sa[Rank[i]-1];//得到排名在A排名的一位的后缀B,其所在的字符串的开始位置 
            while(s[j+len]==s[i+len])  //如果对应位置相等则Len++ 
    		      ++len;
            h[Rank[i]]=len;
    		//从源串中第i个位置开始的后缀,其与排名在它前面的那个后缀,两者的Lcp为Len 
        }
    }
    int main()
    {
        scanf("%d%s",&n,s+1);m=127;
        for(int i=1;i<=n;i++)a[i]=s[i];
        get_sa();get_lcp();int ans=-1;
        for(int i=1;i<=n;i++)ans=max(ans,h[i]);
        printf("%d",ans);
    }
    

      

  • 相关阅读:
    hive之external table创建
    hive之managed table创建
    Ubuntu下hadoop1.0.4安装过程
    hadoop相关Exception
    ASP.NET 数据访问类 SQLSERVER
    ASP.NET中Cookie编程的基础知识
    SourceForge上的好东西(.Net)
    ASP.NET生成高质量缩略图通用函数(c#代码)
    Sql Server实用操作小技巧集合
    分页SQL Server存储过程
  • 原文地址:https://www.cnblogs.com/cutemush/p/12336303.html
Copyright © 2011-2022 走看看