1.假设字符串尾 "ABABABB"
那么它的所有后缀为:
ABABABB
BABABB
ABABB
BABB
ABB
BB
B
将上面的所有后缀排好序之后变成:
ABABABB
ABABB
ABB
B
BABABB
BABB
BB
高度数组为所有后缀排好序之后的相邻两个后缀之间的最大公共前缀
比如height[1],看下标为1的后缀ABABB与上一个下标0的后缀ABABABB,最大公共前缀为ABAB,四个,那么height[1] = 4
其余的也是一样,那么可以得到高度数组为height[] = {0,4,2,0,1,3,1}
2.使用代码描述
假设字符串为ABABABB(str),rank为下标的排名数组,rk[2] = 3表示下标为2的后缀排名是3,已经把字符串str的所有后缀排好序之后的数组为sa[](sa[3] = 5表示排名为3的后缀后缀下标为5)
先把外层循环搞定i从0-src.length-1
因为原来的sa数组是按照字符串相同排名相同,现在调整排名为不重复的排名,重新排名后得到数组rk
紧扣循环中的下标i,得到下标为i的排名rk[i] 在得到该排名的上一次排名rk[i]-1
(存在的规律是上一个下标i假如有k个公共前缀,并且k>0,那么下一个下标至少有一个k-1个公共前缀,那么前k个字符是不用比较的)
具体的代码如下:
public class 高度数组{
public static int [] getHeight(String src,Suff sa[]){
int height[] = new int[src.length()];
int rk[] = new int[src.length()];
//恢复不重复的排名
for(int i = 0;i<src.length();i++){
rk[sa[i].index] = i;
}
int rk_i;
int rk_i_1;
int k = 0;
for(int i = 0;i<src.length();i++){
rk_i = rk[i];
if(rk_i==0){
height[0] = 0;
continue;
}
//下标为i的排名的上一个排名
rk_i_1 = rk_i - 1;
//求出排名为rk_i_1的下标
int j = sa[rk_i_1].index;
if(k>0)k--;
for(;j+k<src.length()&&i+k<src.length();k++){
if(src.charAt(j+k)!=src.charAt(i+k)){
break;
}
}
height[rk_i] = k;
}
return height;
}
}
其中绑定了下标的类的代码如下:
import java.util.Arrays;
public class Suff implements Comparable<Suff>{
public String str;
public int index;//后缀的起始下标
public Suff(String str, int index) {
this.str = str;
this.index = index;
}
public String toString() {
return "Suff{" +
"char='" + str + '\'' +
", index=" + index +
'}';
}
//直接对所有后缀排序,因为字符串的比较消耗O(n),所以整体为n^2log(n)
public static Suff[] getSa(String src){
int strLength = src.length();
//sa是排名到下标的映射,即sa[i] = k说明排名为i的后缀是从k开始的
Suff suffixArray[] = new Suff[strLength];
for(int i =0;i<strLength;i++){
String suffI = src.substring(i);
suffixArray[i] = new Suff(suffI,i);
}
Arrays.sort(suffixArray);//依据suff的排序规则进行排序
return suffixArray;
}
@Override
public int compareTo(Suff o2) {
return this.str.compareTo(o2.str);
}
}