可重叠最长重复子串
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); }
以上是关于可重叠最长重复子串的主要内容,如果未能解决你的问题,请参考以下文章
POJ 3261 Milk Patterns(后缀数组[可重叠的k次最长重复子串])
POJ 3261 Milk Patterns(后缀数组[可重叠的k次最长重复子串])