题目链接:http://www.spoj.pl/problems/PHRASES/
题意:给定n个字符串,求在每个字符串中至少出现两次且不重叠的最长字串。
思路:先将n个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求sa和height数组。然后二分答案,将后缀分组。判断的时候,要看是否有一组后缀在每个原来的字符串中至少出现两次,并且在每个原来的字符串中,后缀的起始位置的最大值与最小值之差是否不小于当前答案。
int r[N],sa[N],wa[N],wb[N],wd[N],rank[N],h[N];
int cmp(int *r,int a,int b,int L)
{
return r[a]==r[b]&&r[a+L]==r[b+L];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
FOR0(i,m) wd[i]=0;
FOR0(i,n) wd[x[i]=r[i]]++;
FOR1(i,m-1) wd[i]+=wd[i-1];
FORL0(i,n-1) sa[--wd[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,m=p)
{
p=0;
FOR(i,n-j,n-1) y[p++]=i;
FOR0(i,n) if(sa[i]>=j) y[p++]=sa[i]-j;
FOR0(i,m) wd[i]=0;
FOR0(i,n) wd[x[i]]++;
FOR1(i,m-1) wd[i]+=wd[i-1];
FORL0(i,n-1) sa[--wd[x[y[i]]]]=y[i];
t=x;x=y;y=t;p=1;x[sa[0]]=0;
FOR1(i,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
void calHeight(int *r,int *sa,int n)
{
int i,j,k=0;
FOR1(i,n) rank[sa[i]]=i;
FOR0(i,n)
{
if(k) k--;
j=sa[rank[i]-1];
while(i+k<n&&j+k<n&&r[i+k]==r[j+k]) k++;
h[rank[i]]=k;
}
}
char s[N];
int n,b[N],M;
int OK(int mid)
{
int i,j,k,t,p[15][2],L,R;
FOR1(i,M)
{
L=i;
while(L<=M&&h[L]<mid) L++;
R=L;
while(R<=M&&h[R]>=mid) R++;
FOR0(j,n) p[j][0]=INF,p[j][1]=-INF;
FOR(j,L-1,R-1)
{
k=b[sa[j]];
if(k==n) continue;
p[k][0]=min(p[k][0],sa[j]);
p[k][1]=max(p[k][1],sa[j]);
}
FOR0(j,n)
{
if(p[j][0]==INF) break;
if(p[j][1]-p[j][0]<mid) break;
}
if(j>=n) return 1;
i=R;
}
return 0;
}
void deal()
{
int low=1,high=10000,mid;
while(low<=high)
{
mid=(low+high)>>1;
if(OK(mid)) low=mid+1;
else high=mid-1;
}
if(OK(low)) PR(low);
else PR(high);
}
int main()
{
int C;
RD(C);
while(C--)
{
RD(n);
int L=0,i,j;
FOR0(i,n)
{
RD(s);
for(j=0;s[j];j++) r[L]=s[j]+100,b[L++]=i;
if(i<n-1) r[L]=i+1,b[L++]=n;
}
r[L]=0;
da(r,sa,L+1,250);
calHeight(r,sa,L);
M=L;
deal();
}
return 0;
}