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);
    }
    

      

  • 相关阅读:
    windows中dos命令指南
    HDU 2084 数塔 (dp)
    HDU 1176 免费馅饼 (dp)
    HDU 1004 Let the Balloon Rise (map)
    变态杀人狂 (数学)
    HDU 2717 Catch That Cow (深搜)
    HDU 1234 开门人和关门人 (模拟)
    HDU 1070 Milk (模拟)
    HDU 1175 连连看 (深搜+剪枝)
    HDU 1159 Common Subsequence (dp)
  • 原文地址:https://www.cnblogs.com/cutemush/p/12336303.html
Copyright © 2011-2022 走看看