高度数组 详解

22 阅读2分钟

(可能需要一点 后缀数组的知识 后缀数组 详解

高度数组:指由后缀数组中相邻两个后缀的最长公共前缀的长度组成的数组
字符串 S 为 abeacadabea
sa: 11 10 7 0 3 5 8 1 4 6 9 2 ( 第 i 大的后缀子串,是从 sa [ i ] 位置开始的子串 )
rk: 3 7 11 4 8 5 9 2 6 10 1 0 ( 第 rk [ i ] 大的后缀子串,是从 i 位置开始的子串 )

按sa的顺序列出来:
“”( 空串 )
“a”
“abra”
“abracadabra”
“acadabra”
“adabra”
“bra”
“bracadabra”
“cadabra”
“dabra”
“ra”
“racadabra”

则高度数组 lcp: 0 1 4 1 1 0 8 1 4 6 9 2
0: 即 “” 和 “a” 的最长公共前缀长度为 0
1: 即 “a” 和 “abra” 的最长公共前缀长度为 1
4: 即 “abra” 和 “abracadabra” 的最长公共前缀长度为 4

如果我们一个个比较的话,复杂度为 O( n * n ),这样复杂度有点高,我们怎样可以降低复杂度呢?
可以从减少重复的字符比较入手

我们从 最长的后缀 向 最短的后缀 处理,每次都和比 该后缀排序的前一位 进行比较
例:
第一次比:“abracadabra” 和 “abra”
第二次比:“bracadabra” 和 “bra”
第三次比:“racadabra” 和 “ra”
第四次比:“acadabra” 和 “abracadabra”

我们不难发现,假设我们第 i 次比较的是后缀子字符串 a 和 b ,结果是最长公共前缀为 h
则我们第 i + 1 次比较时,则结果至少为 h - 1,因为 a 减少开头字符时,一定有一个 b 减少开头字符的后缀与其 最长公共后缀为 h - 1 ( 当然也有可能更长 ),所以我们只用从 h - 1 处开始继续比较字符即可,这样不就减少了重复的字符比较

复杂度为 O( n )
代码如下:

int lcp[105];
void construct_lcp()
{
    int h = 0;
    for(int i = 0; i < S.length(); i++)
    {
        int j = rk[i] - 1;
        if(h != 0) h--;
        while(i + h < S.length() && sa[j] + h < S.length() && S[sa[j]+h] == S[i+h]) h++;
        lcp[j] = h;
    }
}

代码很简单,就是可能 rk,sa 用来用去有点绕,j 就是 i 字典序前面的那个排在第几个,sa [ j ] 就是 i 字典序前面的那个是从第几个字符开始后缀子字符串 ( 不用比空串,空串是字典序第一个,前面没了 )