后缀数组构建--倍增算法分析

对于后缀数组的了解,要始于一篇论文,论文中,使用后缀数组的方式存储索引,改进词组检索的效果和性能,相对于倒排索引而言,后缀数组在空间占用上会比较大,但是效果却非常好,这个是倒排索引所无法高效应对的,而且,个人的一个猜想,会有越来越多的内存索引是基于后缀数组实现的,或许,已经有了。 从别的地方转来的几个个概念,我就不重复了: 子串:字符串S的子串r[i..j],i≤j,表示r串中从i到j这一段,也就是顺次排列r[i],r[i+1],...,r[j]形成的字符串。 后缀:后缀是指从某个位置i开始到整个串末尾结束的一个特殊子串。字符串r的从第i个字符开始的后缀表示为Suffix(i),也就是Suffix(i)=r[i..len(r)]。 后缀数组:后缀数组SA是一个一维数组,它保存1..n的某个排列SA[1],SA[2],……,SA[n],并且保证 Suffix(SA[i])<Suffix(SA[i+1]),1≤i<n。也就是将S的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA中。 名次(rank)数组:名次数组Rank[i]保存的是Suffix(i)在所有后缀中从小到大排列的“名次”。 从定义中,可以看出,后缀数组和名次数组是互逆的。这一点至关重要,无论是倍增算法,还是求height数组的时候,都要能够熟练的理解这个,就好像潜意识中一样。 下面就说一下倍增算法,主要思路如下:用倍增的方法对每个字符开始的长度为2k的子字符串进行排序,求出排名,即rank值。k从0开始,每次加1,当2k大于n以后,每个字符开始的长度为2k的子字符串便相当于所有的后缀。并且这些子字符串都一定已经比较出大小,即rank值中没有相同的值,那么此时的rank值就是最后的结果。可以通过如下的手绘的图,来理解这个过程。同样以aabaaaab为例: 每一次,都是对每个字符开始的2^k的字串,按照字典序进行排序,比说第3次,每个字符开始的字符串长度就应该为8,则分别为,aabaaaab,abaaaab,baaaab,aaaab, aaab,aab,ab,b。首先补全,要在后面全部补a,字典序结果为,4,6,7,1,2,3,5,7。 根据以上的思路,不做改进,实现的代码,效率会非常低。主要原因在于,每次子字符串排序的时候,效率比较低(循环次数logn,排序时间O(nlogn),总的时间复杂度为O(n(ogn)^2))。下面章介绍使用基数排序的倍增算法,会对基数排序是如何提速的。 基本的想法就是,2^k的子串可以表示为两个2^(k-1)的子串。这样排序就可以使用基数排序,时间复杂度为O (nlog(r)m)。并且,可以利用到上次的排序结果,这一步排序的时间复杂度变为O(n),则总的时间复杂度为O(nlogn),过程示意图如下: 这个图转自另一个博客,把过程已经很明显的表达出来了。 【引用】

http://www.cppblog.com/superKiki/archive/2010/05/15/115421.html

posted on 2011-12-25 03:19  sing1ee  阅读(307)  评论(0)    收藏  举报