可重叠最长重复子串

Posted cutemush

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可重叠最长重复子串相关的知识,希望对你有一定的参考价值。

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

  

以上是关于可重叠最长重复子串的主要内容,如果未能解决你的问题,请参考以下文章

可重叠的k次最长重复子串

不可重叠最长重复子串

POJ 3261 Milk Patterns(后缀数组[可重叠的k次最长重复子串])

POJ 3261 Milk Patterns(后缀数组[可重叠的k次最长重复子串])

hiho120 后缀数组一·重复旋律2字符串--后缀数组--最长不可重叠重复子串问题

hiho121 后缀数组一·重复旋律2字符串--后缀数组--最长可重叠重复K次子串问题