一道论文题,感觉有点玄学。
一个值得借鉴的思想就是:每两个相邻数做差值从而判断一段数列的差值数列是否相等。
分组,对于一定的len值,按照名次后缀排列下来的话,每次遇到height<len便分组,在一组中hegiht必然都>=len,现在记录sa的最大和最小值,判断其差值是否大于len(注意不能等于,因为题目规定需要相隔一位),若大于则必然有两个后缀不会重叠且len满足要求。而这个len正是所求的未知数,所以想到了二分答案。
感觉不太会写二分了233,发现第一个差值不能选,差值的第一项应为原数列的第二项减第一项,这点应该特别注意。然后因为二分的问题WA了一屏,最后还是借鉴了WABoss的代码将mid值修改了下就过了。。。
(抄下来的,我也不知道对不对:)
- 如果是求最小解,mid要取floor,即mid=(left+right)/2
- 如果是求最大解,mid要取ceil,即mid=(left+right+1)/2
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n,m;
int c[maxn<<1];
int s[maxn<<1];
char ss[maxn<<1];
int sa[maxn<<1],rank[maxn<<1],height[maxn<<1];
int tmp[maxn<<1],temp[maxn<<1];
void getsa()
{
int *x=tmp,*y=temp,cnt;
int m=30;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]=s[i]]++;
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
cnt=0;
for(int i=n-k+1;i<=n;i++)y[++cnt]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++cnt]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[y[i]]]++;
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i];
swap(x,y);
x[sa[1]]=cnt=1;
for(int i=2;i<=n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?cnt:++cnt;
if(cnt>=n)break;
m=cnt;
}
cnt=0;
for(int i=1;i<=n;i++)rank[sa[i]]=i;
for(int i=1;i<=n;i++)
{
if(cnt)cnt--;
int j=sa[rank[i]-1];
while(s[i+cnt]==s[j+cnt])cnt++;
height[rank[i]]=cnt;
}
}
int main()
{
scanf("%s",ss+1);
int len1=strlen(ss+1);
for(int i=1;i<=len1;i++)
s[i]=ss[i]-'a'+2;
s[len1+1]=1;
scanf("%s",ss+1);
int len2=strlen(ss+1);
for(int i=1;i<=len2;i++)
s[len1+i+1]=ss[i]-'a'+2;
n=len1+len2+1;
getsa();
//for(int i=1;i<=n;i++)cout<<height[rank[i]]<<'\n';puts("");
int ans=0;
for(int i=1;i<=n;i++)
{
if(height[i]>ans&&((sa[i]>=len1&&sa[i-1]<len1)||(sa[i]<len1&&sa[i-1]>=len1)))
ans=height[i];
}
printf("%d",ans);
}