由于后缀的数组的构造过程比较精妙,代码也经过了很多的优化,一下子不怎么容易看懂,我就把它化整为零,一点一点来做!
首先还是倍增算法的思想:
综合许大牛与罗大牛的论文,应该是很容易懂的!倍增其实就是排序的一种策略、这种策略要求对基数排序的思想有所理解!如果不懂基数排序,我觉得不用急着去学习后缀数组了!先把基础搭起来再往下走!
第一句话:倍增充分利用了后缀之间的联系!
在许大牛的论文对这一点论述得很清楚:我们需要用到前缀。我们把问题具体化,如果需要对字符串abaas的后缀进行排序:该怎么做呢?
首先在字符串的后面加一个字符$,字符串就变成了:abaas$,它的后缀分别是
1、abaas$ 2、baas$ 3、aas$ 4、as$ 5、s$ 6、$
我们看一下下面这种思路:
1、先比较上面6个字符串的第一个字符:a b a a s $,在只比较第一个字符的前提下,(注意:只比较第一个字符,不考虑后面的),我们很容易看出:第1、3、4三个字符串相等,而第2、5、6两个字符串不相等。这样我们就可以写出sa_1和rank_1的结果了:
sa_1={index(abaas),index(aas),index(as),index(baas),index(s),index($)};
rank_1={2,3,2,2,4,1};
这里:index(s)表示后缀s在原字符串中的起始位置。
rand_1[i]表示第i个后缀在所有后缀中的排名:比如rank_1[5]=0,就表示字符串abaas$的第5个后缀,即字符串 $ 在这6个后缀中排名第一,是最小的!
2、对于第一次比较不相等的两个字符串,实际上我们已经比较出他们的大小了,可以剔到一边去了。我们现在需要做的是考虑相等的三个字符串:abaas$ aas$ as$。
按与上面相同的方法,我们比较字符串的第二个字符:b a a s $ 0。最后的那个后缀只有一个字符,所以我们补0,这个时候,我们对比一下第一次比较时提取出的字符:a b a a s $:
a b a a s $
b a a s $ 0 这里我故意错位,就能发现,第一次的rank,即rank_1已经保存了我们这次比较的前5个排名 3,2,2,4,1。
对于最后的补0,我们完全可以接到最后!这样我们第二次比较的排名就可以这样表示:
第一关键字:2,3,2,2,4,1
第二关键字:3,2,2,4,1,0
综合起来就是这样的: 23,32,22,24,41,10 ,这个不直观,我们还是让排名从1开始:
3 5 2 4 6 1
所以rank_2就变成了:rank_2={3,5,2,4,6,1};里面的排名没有重复的了,而所对于一个字符串来说,任意两个后缀是不可能相等的。所以我们的排序就剩下最后一步了:
根据sa与rank的互逆关系:假如sa[i]=k,那么rank[k]=i我们很容易得到:
sa={6,3,1,4,2,5}; 字符串下标从0开始,所以sa={5,2,0,3,1,4}
所以整个字符串排序的结果就出来了(从小到大排序):
1、$ 2、aas$ 3、abaas$ 4、as$ 5、baas$ 6、s$