复习了后缀数组。
首先因为转调我们需要对字符串作差。
考虑二分+后缀数组的做法。
二分答案k,将连续的height[i]>=x的段分组,如果一组内sa的最大值与最小值的差>=k,则k可行。
后缀自动机也是可以的。
因为right集合即为子串在母串中出现位置的右端点的集合,求出right集合就可以确定串的出现位置,那么求出每个点right集合中的最大值和最小值,如果他们的差值不小于该点的max,那么就说明这个位置代表的子串重复出现且没有重合,用max更新答案即可(因为max是这个节点所包含的子串中最长的那个,如果这个节点所包含的子串的最早出现位置和最晚出现位置的差比max多的话,说明中间塞得下,所以最少有两个以上的不重合子串)。
个人认为后缀自动机比较好想。
后缀数组:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[21000];
int rsort[21000],Rank[21000],sa[21000],y[21000],wr[21000];
bool cmp(int k1,int k2,int ln)
{
return wr[k1]==wr[k2]&&wr[k1+ln]==wr[k2+ln];
}
void get_sa(int n,int m)
{
memset(rsort,0,sizeof(rsort));
for(int i=1;i<=n;i++) Rank[i]=a[i];
for(int i=1;i<=n;i++) rsort[Rank[i]]++;
for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i;
int p=0,k=0,ln=1;
while (p<n)
{
k=0;
for(int i=n-ln+1;i<=n;i++) y[++k]=i;
for(int i=1;i<=n;i++) if (sa[i]>ln) y[++k]=sa[i]-ln;
memset(rsort,0,sizeof(rsort));
for(int i=1;i<=n;i++) rsort[Rank[y[i]]]++;
for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1];
for(int i=n;i>=1;i--) sa[rsort[Rank[y[i]]]--]=y[i];
for(int i=1;i<=n;i++) wr[i]=Rank[i];
Rank[sa[1]]=1;p=1;
for(int i=2;i<=n;i++)
{
if (cmp(sa[i],sa[i-1],ln)==0) p++;
Rank[sa[i]]=p;
}
ln*=2;m=p;
}
}
int height[21000];
void get_he(int n)
{
int k=0;
for (int i=1;i<=n;i++)//位置
{
int j=sa[Rank[i]-1];
if (k) k--;
while (a[i+k]==a[j+k]) k++;
height[Rank[i]]=k;
}
}
bool check(int k,int n)
{
for(int i=2;i<=n;i++)
{
if(height[i]<k) continue;
for(int j=i-1;j>=1;j--)
{
if(abs(sa[i]-sa[j])>=k) return true;
if(height[j]<k) break;
}
}
return false;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF && n)
{
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int mx=-1e8;
for(int i=1;i<n;i++)
{
a[i]=a[i+1]-a[i]+88;
mx=max(mx,a[i]);
}
get_sa(n,mx);
get_he(n);
int ans=1,l=1,r=n;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid,n)==1)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
if (ans+1>=5) printf("%d\n",ans+1);
else printf("0\n");
}
return 0;
}
后缀自动机:
试了试数组版的SAM,感觉没有结构体版的写的爽啊。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
const int N=40003;
using namespace std;
int n,m,cnt,last,root,a[N],Rs[N],sa[N];
int pre[N],mxx[N],ch[N][180],mx[N],mn[N];
void extend(int k)
{
int x=a[k];
int p=last; int np=++cnt; last=np;
mxx[np]=mxx[p]+1;
while(p && !ch[p][x] ) ch[p][x]=np,p=pre[p];
if(!p) pre[np]=root;
else
{
int q=ch[p][x];
if (mxx[q]==mxx[p]+1) pre[np]=q;
else
{
int nq=++cnt;
mxx[nq]=mxx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(p && ch[p][x]==q) ch[p][x]=nq,p=pre[p];
}
}
}
int solve()
{
memset(Rs,0,sizeof(Rs));
for(int i=1;i<=cnt;i++) Rs[mxx[i]]++;
for(int i=1;i<=n;i++) Rs[i]+=Rs[i-1];
for(int i=1;i<=cnt;i++) sa[Rs[mxx[i]]--]=i;
int p=1;
memset(mx,0,sizeof(mx));
memset(mn,127,sizeof(mn));
for(int i=1;i<=n;i++)
{
p=ch[p][a[i]];
mx[p]=mn[p]=i;
}
for(int i=cnt;i;i--)
{
int t=sa[i];
mx[pre[t]]=max(mx[pre[t]],mx[t]);
mn[pre[t]]=min(mn[pre[t]],mn[t]);
}
int ans=0;
for(int i=1;i<=cnt;i++) if(mx[i]-mn[i]>=mxx[i]) ans=max(ans,mxx[i]);
ans++;
if (ans<5) ans=0;
return ans;
}
int main()
{
// freopen("a1.in","r",stdin);
// freopen("a1.out","w",stdout);
while(1)
{
scanf("%d",&n);
if(!n) break;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) a[i]=a[i+1]-a[i]+88;
n--;
memset(mxx,0,sizeof(mxx));
memset(pre,0,sizeof(pre));
memset(ch,0,sizeof(ch));
cnt=0; last=root=++cnt;
for (int i=1;i<=n;i++) extend(i);
printf("%d\n",solve());
}
}