POJ 2217 Secretary(后缀数组+高度数组)

题目链接:

2217 Secretary

思路:

将两个字符串用一个不会出现的字符,如$连接起来,然后求此字符串的高度数组,从前往后遍历所有i,当sa[i]sa[i+1即两个子串的起始位置在两个不同的字符串中时(即我们相加的这两个字符串),此时iheight即为公共子串长度,我们找到最大值即可

代码:

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2e4+99;
string s,a,b;
int n,k,sa[maxn],rk[maxn],tmp[maxn],lcp[maxn];
bool cmp(const int& i,const int& j){
	if(rk[i]!=rk[j]) return rk[i]<rk[j];
	else{
		int ri=i+k<=n?rk[i+k]:-1;
		int rj=j+k<=n?rk[j+k]:-1;
		return ri<rj;
	}
}
void cal_sa(){
	n=s.length(); 
	for(int i=0;i<=n;i++) sa[i]=i,rk[i]=s[i]; rk[n]=0;
	for(k=1;k<=n;k<<=1){
		sort(sa,sa+n+1,cmp);
		tmp[sa[0]]=0;
		for(int i=1;i<=n;i++){
			tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
		}
		for(int i=0;i<=n;i++) rk[i]=tmp[i];
	}
}
void cal_lcp(){
	for(int i=0;i<=n;i++) rk[sa[i]]=i;
	int h=0; lcp[0]=0;
	for(int i=0;i<n;i++){
		int j=sa[rk[i]-1];
		if(h) --h;
		while(j+h<n&&i+h<n&&s[j+h]==s[i+h]) h++;
		lcp[rk[i]-1]=h;
	}
}
void solve(){
	s=a+'$'+b; cal_sa(); cal_lcp();
	int ans=0,len=a.length();
	for(int i=0;i<s.length();i++){
		if((sa[i]<len)!=(sa[i+1]<len)) ans=max(ans,lcp[i]);
	}
	cout<<"Nejdelsi spolecny retezec ma delku "<<ans<<".
";
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
	int kase; cin>>kase; getline(cin,s);
	while(kase--){
		getline(cin,a); getline(cin,b);
		solve();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/yuhan-blog/p/12308726.html