这道题是后缀数组的经典题。通过做这道题,对后缀数组的理解更深了一层。
先附上大佬博客Orz:
https://blog.csdn.net/yxuanwkeith/article/details/50636898
https://www.cnblogs.com/WABoss/p/5199261.html
题意:找出不可重叠的最长重复子串
主要是:先二分答案,判别长度为k是否符合要求。把排序后的后缀分成若干组(height的值由大变小),其中每组的后缀之间的Height值应不小于k。再判断不重复的后缀,两个子串要隔至少一个位置,最大的SA值和最小的SA值相差应大于k。
我还是太菜了。。二分还是不会写啊55555...试了好几种方法才弄对QAQ
最难过的一点是,,抄错板子了!!!!!哇呜呜呜呜......
附上AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
typedef pair<int,int>pp;
#define mkp make_pair
#define pb push_back
const int INF=0x3f3f3f3f;
const ll MOD=1e9+(ll)7;
const int MAX=20005;
int n;
int a[MAX];
int t1[MAX],t2[MAX],c[MAX];
bool cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
int sa[MAX];
int ran[MAX];
int height[MAX];
void da(int str[],int n,int m)
{
n++;
int i,j,p,*x=t1,*y=t2;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=str[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
for(i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=j)
y[p++]=sa[i]-j;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
if(p>=n)
break;
m=p;
}
int k=0;
n--;
for(i=0;i<=n;i++)
ran[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)
k--;
j=sa[ran[i]-1];
while(str[i+k]==str[j+k])
k++;
height[ran[i]]=k;
}
}
int r[MAX];
bool check(int mid);
void solve(int str[],int n,int m)
{
for(int i=0;i<n;i++)
r[i]=str[i];
r[n]=0;//n++;
memset(sa,0,sizeof(sa));
memset(ran,0,sizeof(ran));
memset(height,0,sizeof(height));
da(r,n,m);
int l=0,r=n+1,mid;
while(l<=r)
{
mid=(l+r)/2;
//cout<<" l="<<l<<" r="<<r<<" mid="<<mid<<endl;
if(check(mid))
l=mid+1;
else
r=mid-1;
}
l--;
if(l+1<5)
printf("0\n");
else
printf("%d\n",l+1);
}
bool check(int k)
{
int mi=INF,ma=-INF;
for(int i=2;i<=n;i++)
{
if(height[i]>=k)//最长公共前缀>=k
{ //判断不重叠
mi=min(mi,min(sa[i],sa[i-1]));
ma=max(ma,max(sa[i],sa[i-1]));
if(ma-mi>k)//两个子串要隔至少一个位置,不能"="
return true;
}
else //分组
mi=INF,ma=-INF;
}
return false;//k偏大
}
int sub[MAX];
int main()
{
while(scanf("%d",&n)==1)
{
if(n==0)
break;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(i>0)
sub[i-1]=a[i]-a[i-1]+88;//防止为负
}
n--;
solve(sub,n,176); //max=87+88
}
return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
*/
另外附上后缀数组的正确板子。。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
typedef pair<int,int>pp;
#define mkp make_pair
#define pb push_back
const int INF=0x3f3f3f3f;
const ll MOD=1e9+(ll)7;
const int MAX=20005;
int n;
int a[MAX];
int t1[MAX],t2[MAX],c[MAX];
bool cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
int sa[MAX];
int ran[MAX];
int height[MAX];
void da(int str[],int n,int m)
{
n++;
int i,j,p,*x=t1,*y=t2;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[i]=str[i]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
for(i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=j)
y[p++]=sa[i]-j;
for(i=0;i<m;i++) c[i]=0;
for(i=0;i<n;i++) c[x[y[i]]]++;
for(i=1;i<m;i++) c[i]+=c[i-1];
for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
if(p>=n)
break;
m=p;
}
int k=0;
n--;
for(i=0;i<=n;i++)
ran[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)
k--;
j=sa[ran[i]-1];
while(str[i+k]==str[j+k])
k++;
height[ran[i]]=k;
}
}
int rmq[MAX];
int mm[MAX];
int best[20][MAX];
void initRMQ(int n)
{
mm[0]=-1;
for(int i=1;i<=n;i++)
{
mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
best[0][i]=i;
}
for(int i=1;i<=mm[n];i++)
for(int j=1;j+(1<<i)-1<=n;j++)
{
int a=best[i-1][j];
int b=best[i-1][j+(1<<(i-1))];
if(rmq[a]<rmq[b])
best[i][j]=a;
else
best[i][j]=b;
}
}
int askRMQ(int a,int b)
{
int k=mm[b-a+1];
b-=(1<<k)-1;
a=best[k][a];b=best[k][b];
return rmq[a]<rmq[b]?a:b;
}
int lcp(int a,int b)
{
a=ran[a];b=ran[b];
if(a>b)
swap(a,b);
return height[askRMQ(a+1,b)];
}
int r[MAX];
void solve(int str[],int n,int m)
{
for(int i=0;i<n;i++)
r[i]=str[i];
r[n]=0;
memset(sa,0,sizeof(sa));
memset(ran,0,sizeof(ran));
memset(height,0,sizeof(height));
da(r,n,m);
for(int i=1;i<=n;i++)
rmq[i]=height[i];
initRMQ(n);
/*for(int i=1;i<=n;i++)
cout<<"i="<<i<<" sa="<<sa[i]<<endl;
for(int i=1;i<=n;i++)
cout<<"i="<<i<<" height="<<height[i]<<endl;*/
}
int sub[MAX];
int main()
{
while(scanf("%d",&n)==1)
{
if(n==0)
break;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
if(i>0)
sub[i-1]=a[i]-a[i-1]+88;
}
n--;
solve(sub,n,176); //max=87+88
}
return 0;
}