先考虑一个简单的问题,计算一个字符串中至少出现两次以上的最长子串,答案一定会在后缀数组中相邻两个后缀的公共前缀之中,所有只要考虑他们就好了,原因是子串的开始位置在后缀中相距越远,其公共前缀的长度也就越短,因此,高度数组的最大值就是答案。
再考虑这个问题的解法,因为对于两个字符串,不好直接运用后缀数组,所以我i们可以在S,T中间插入一个不会出现的字符('\0')拼成一个字符串 S'' ,然后,计算 S‘’ 的后缀数组,检查后缀数组的所有相邻后缀,其中,分属于S和T的不同字符串的后缀的lcp的最大值就是答案。而要知道后缀时属于S还是T,可以由其在 S'' 中的位置直接判断。
#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
int rankk[maxn];
int tmp[maxn];
int k,n;
string s,t;
int sa[maxn],lcp[maxn];
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(string s,int *sa){
for(int i=0;i<=n;i++){
sa[i]=i;
rankk[i]=i<n?s[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(string s,int *sa,int *lcp){
int n=s.size();
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) h--;
for(;j+h<n&&i+h<n;h++){
if(s[j+h]!=s[i+h]) break;
}
lcp[rankk[i]-1]=h;
}
}
void solve(){
int s1=s.size();
s=s+'\0'+t;
construct_sa(s,sa);
construct_lcp(s,sa,lcp);
int ans=0;
for(int i=0;i<n;i++){
if((sa[i]<s1)!=(sa[i+1]<s1)){
ans=max(ans,lcp[i]);
}
}
cout<<"Nejdelsi spolecny retezec ma delku "<<ans<<"."<<endl;
}
int main(){
ios::sync_with_stdio(0);
int cs;
cin>>cs;
cin.ignore();
while(cs--){
getline(cin,s);
getline(cin,t);
n=s.size()+t.size()+1;
solve();
}
return 0;
}