后缀数组1

这一篇和下一篇博文我准备写一下我在参加ACM/ICPC期间曾经研究过的后缀数组。关于后缀数组,网上有很多英文资料,但是很多现在的研究结果都是受1991年Udi Manber & Gene Myers的《Suffix arrays: a new method for on-line string searches》中所提的方法的启发,采用倍增的思想。当然,现在有国外学者提到的三分+分治的线性构造方法除外。

写这两篇文章主要是为了把一种思想写下来,同时练练已经生疏了很久的算法。我曾经在老的博客上写过一个比较丑陋的后缀数组构造算法,在后一篇文章中我将结合近期我看到的资料对它进行优化,使其变得比较美观 :-)
我们定义一个字符串A,其表示:

A = a0a1a2…an-1

其中ai(0 ≤ i < n)都是字符集E中的字符,这个字符集是全序的,也就是说其中任意两个字符都是可以比较大小的。例如,用ASCII码编码的英文字符集就是这样的一个E的特例。

|A|表示字符串的长度,其值为n;Ai(0 ≤ i < n)表示一个后缀,它其实表示一个字符串:

Ai = aiai+1ai+2…an-1

我们让运算符“≤”表示两个串按照字典序比较,然后定义运算符“≤h”表示两个串的前h个字符按照字典序比较(=h、<h等同理),那么就有:

结论1 若Aj =h Ak且Aj+hh Ak+h,则Aj2h Ak (j+h, k+h < n,“≤”换成“=、<、>”等等依然成立)

这个是1989年Udi Manber & Gene Myers发明 nlogn 后缀数组生成算法的主要理论依据。然而他们的天才之处不是在于看到了这个结论而是将这个结论与“复用”的思想结合在了一起。
什么是后缀数组呢?

后缀数组(Suffix Array)是一个存放索引的数组,如果把这个数组命名为SA,那么有:

A[SA[x]] ≤ A[SA[y]],其中0 ≤ x ≤ y < n

要产生这样一个数组,我们可以用最普通的sort/qsort结合strcmp,这个看起来是一个不错的想法,但是考虑到strcmp其实不是常数时间复杂度而是线性时间复杂度的,所以这个算法就显得不是那么高效了。Udi Manber & Gene Myers的聪明之处就是将结论1中的h取成了“1、2、4、8、16…”这样的指数数列,只要每趟比较保证字符串A的所有后缀按≤h有序,那么相应地,每个后缀扩展成2h长度之后,只要比较其后h个字符就行了,而这后h个字符其实就是其他某个后缀的前h个字符,其实已经比较过了,直接结合结论1可以得出结果,仅仅花了常数时间。这样,经过 logn 趟比较,后缀数组就生成了。例如:

A = abba
A0 = abba
A1 = bba
A2 = ba
A3 = a

h = 1 时,A0 A1 A2 A3 的≤h有序序列为:A0 =h A3 <h A2 =h A1
h = 2 时,要决定A1与A2的≤2h比较结果,因为 A2 =h A1,根据结论1,只要看a与ba的≤h比较结果就行了,而我们欣喜地发现,这个结果其实就是A2与A3的≤h比较结果,在h = 1的时候早就得出了结论——A3 <h A2!

所以,我们只要经过 logn 趟比较,每趟比较花费O(n)的时间进行2个关键字的桶排序,那么就可以得到一个后缀数组了!

PS:现在有更好的线性的结果了,但是算法相对复杂,我也没有怎么看过 :-)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值