先考虑一个串中出现过两次以上的子串最大长度, 很显然, 出现过两次以上的子串一定是SA数组中相邻两个后缀的前缀, 所以要找最长的一定是高度数组的最大值.
而两个字符串则可以通过拼接成一个字符串来简化成上面一个串的形式, 但是拼接时候要插入在两个字符串中间插入一个不会出现的字符, 这样就能保证我们求出的公共子串一定是只来自于一个串的.
还要判断SA数组中相邻两个后缀是来自两个不同的串的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=10005;
char a[maxn],b[maxn];
int rankk[maxn];
int sa[maxn];
int tmp[maxn];
int lcp[maxn];
int n,k;
//用的是快速排序
bool compare_sa(int i,int j)
{
if(rankk[i]!=rankk[j]){
return rankk[i]<rankk[j];
}else{
int ri=i+k<=n?rankk[i+k]:-1;
int rj=j+k<=n?rankk[j+k]:-1;
return ri<rj;
}
}
void construct_sa()
{
for(int i=0;i<=n;i++){
sa[i]=i;
rankk[i]=i<n?a[i]:-1;
}
for(k=1;k<=n;k<<=1){
sort(sa,sa+n+1,compare_sa);
tmp[sa[0]]=0;
for(int i=1;i<=n;i++){
tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
}
for(int i=0;i<=n;i++){
rankk[i]=tmp[i];
}
}
}
void construct_lcp()
{
for(int i=0;i<=n;i++){
rankk[sa[i]]=i;
}
int h=0;
lcp[0]=0;
for(int i=0;i<n;i++){
int j=sa[rankk[i]-1];
if(h>0){
h--;
}
for(;j+h<n&&i+h<n;h++){
if(a[j+h]!=a[i+h]){
break;
}
}
lcp[rankk[i]-1]=h;
}
}
int main()
{
int t;
scanf("%d",&t);
getchar();
while(t--){
cin.getline(a,maxn);
cin.getline(b,maxn);
int la=strlen(a),lb=strlen(b);
a[la]=0;
copy(b,b+lb,a+la+1);
n=la+lb+1;
construct_sa();
construct_lcp();
int ans=0;
for(int i=0;i<n;i++){
if((sa[i]<la)!=(sa[i+1]<la)){
ans=max(ans,lcp[i]);
}
}
printf("Nejdelsi spolecny retezec ma delku %d.\n",ans);
}
return 0;
}