求两个后缀数组的最长公共前缀,可以转化为求某个区间上的最小值。 接下来,先来构造后缀数组,先看这里。
//求两个后缀的最长公共前缀 <1>
int lcs(int i, int j){
int b, e;
if(RANK[i] > RANK[j])
e=RANK[i], b=RANK[j]+1;
else e=RANK[j], b=RANK[i]+1;
int lcs=height[b++];
for(; b<=e; ++b) lcs=min(lcs, height[b]);
return lcs;
}
如果用RMQ 的 Sparse-Tree 预处理,则可以做到 O(nlogn)。
// RMQ Sparse-Tree 预处理
int m=log(len)/log(2.0)+1; // len是字符串的长度
int dp_min[maxlen][m], dp_max[maxlen][m];
void RMQ_init(){
int i=0,j=0;
for(i=1; i<=len; i++)
dp_max[i][0]=dp_min[i][0]=s[i];
double k=log(n)/log(2.0);
for(i=1; i<(int)k; i++)
for(j=1; j+(1<<i)-1 <= n; i++){
dp_max[j][i] = max(dp_max[i][j-1], dp_max[ j+(1<<(i-1)) ][i-1]);
dp_min[j][i] = min(dp_min[i][j-1], dp_min[ j+(1<<(i-1)) ][i-1]);
}
}
// RMQ :求两个后缀的最长公共前缀
int RMQ_min(int i, int j){
if(i>j)swap(i,j);
int k=int(log(j-i)/log(2.0));
return min(dp_min[i][k], dp_min[ j-(1<<k)+1 ][k]);
}
求可重叠最长重复子串,等价于求两个后缀的最长公共前缀的最大值,因为任意两个后缀的最长公共前缀都是height数组里的某一段的最小值,那么这个值一定不大于height数组里的最大值,所以,最长公共前缀就是height数组里的最大值了。上面已经用RMQ预处理了,那就利用起来把。
// RMQ: 求可重叠最长重复子串
int RMQ_max(int i, int j){
if(i>j) swap(i,j);
int k=int(log(j-i)/log(2.0));
return max(dp_maxp[i][k], dp_max[ j-(1<<k)+1 ][k]);
}
求两个字符串的最长公共子串,先将第二个字符串写在第一个字符串后面,中间用一个没有出现过的字符隔开,再求这个新的字符串的后缀数组,在height[i]是两个字符串的公共前缀的前提下,求出height[i]的最大值。
// 最长公共子串
int longest_common_substring(int inter){
// inter是两串字符串中间的分隔符的位置
int ans = 0;
for(int i = 2; i <= len; ++i)
if((sa[i-1] < inter && sa[i] > inter) ||
(sa[i] > inter && sa[i-1] > inter))
ans = max(ans, height[i]);
// if 判断部分看到别人是这样写的
// if((sa[i-1] - inter)*(sa[i] - inter) < 0)
return ans;
}
参考:罗穗骞 《后缀数组——处理字符串的有力工具》