POJ 2217 (最長公共子串)
阿新 • • 發佈:2018-12-30
先考慮一個簡單的問題,計算一個字串中至少出現兩次以上的最長子串,答案一定會在後綴陣列中相鄰兩個字尾的公共字首之中,所有隻要考慮他們就好了,原因是子串的開始位置在後綴中相距越遠,其公共字首的長度也就越短,因此,高度陣列的最大值就是答案。
再考慮這個問題的解法,因為對於兩個字串,不好直接運用字尾陣列,所以我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; }