求两个后缀数组的最长公共前缀,可以转化为求某个区间上的最小值。 接下来,先来构造后缀数组,先看这里。
//求两个后缀的最长公共前缀 <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; }
参考:罗穗骞 《后缀数组——处理字符串的有力工具》
版权声明:本文为博主原创文章,未经博主允许不得转载。