SA-IS学习笔记

后缀数组SA-IS

S A − I S SA-IS SAIS排序是基于诱导排序这种思想,将问题规模缩小,解决更小的问题,快速解决原问题的算法。


首先给出一些定义:

一 可能用到的

用大写字母表示字符串,小写字母表示字符或位置。
字符串最后用#表示。
在字符串 A A A中, s u f f i x ( A , i ) suffix(A,i) suffix(A,i)表示长度为i的后缀。

二 后缀类型

在字符串 A A A中,对于每一个后缀,我们赋一个类型。
s u f f i x ( A , i ) suffix(A,i) suffix(A,i) S S S型后缀表示 s u f f i x ( A , i ) &lt; s u f f i x ( A , i + 1 ) suffix(A,i) &lt; suffix(A,i+1) suffix(A,i)<suffix(A,i+1)
s u f f i x ( A , i ) suffix(A,i) suffix(A,i) L L L型后缀表示 s u f f i x ( A , i ) &gt; s u f f i x ( A , i + 1 ) suffix(A,i) &gt; suffix(A,i+1) suffix(A,i)>suffix(A,i+1)
我们默认最后一个字符为 S S S
后用t表示后缀类型数组。

三 LMS子串

我们定义*型为 S S S型,且它的左边为 L L L型,即它是一连串 S S S型的开头。
如上面的后缀类型比如以字符串 a a b b a c c aabbacc aabbacc为例:

01234567
Saabbacc#
tSSLLSLLS
**

∗ * 型后缀如图所示。
则LMS子串表示两个*之间的子串
图上子串依次为:“aabba”,“acc#”,"#"。
利用所有的 ∗ * 型后缀,我们得出一个新串 A 1 A1 A1
对于新串 A 1 A1 A1 S A 1 SA1 SA1是它们之间的相对顺序。

01234567
Saabbacc#
tSSLLSLLS
**
A110

如图所示。


接下来给出一些引理:

引理一:

我们要考虑如何快速构造t数组。
首先有一些性质:
对于一个字符串 A A A一个后缀是类型为 S S S,当且仅当下面两项满足:
1. A [ i ] &lt; A [ i + 1 ] A[i]&lt;A[i+1] A[i]<A[i+1]
2. A [ i ] = A [ i + 1 ] A[i]=A[i+1] A[i]=A[i+1] t [ i + 1 ] t[i+1] t[i+1] S S S
同理,对于 L L L型:
1. A [ i ] &gt; A [ i + 1 ] A[i]&gt;A[i+1] A[i]>A[i+1]
2. A [ i ] = A [ i + 1 ] A[i]=A[i+1] A[i]=A[i+1] t [ i + 1 ] t[i+1] t[i+1] L L L
易证。

引理二:

若后缀 A A A S S S型, B B B L L L型,当 A [ 0 ] = B [ 0 ] A[0]=B[0] A[0]=B[0],则 A &gt; B A&gt;B A>B
证明:
A = a b X A=abX A=abX, B = a c Y B=acY B=acY
假设 b ! = a b!=a b!=a c ! = a c!=a c!=a
因为 A A A S S S型,得: a &lt; b a&lt;b a<b
同理: a &gt; c a&gt;c a>c
b &gt; c b&gt;c b>c,所以 A &gt; B A&gt;B A>B
b = a b=a b=a或者 a = c a=c a=c时,也可以根据引理一,推成第一种情况。

引理三:

对于非"#"得 L M S LMS LMS子串长度大于2。
证明:两个 L M S LMS LMS串中间必定含有一个 L L L型后缀。

引理四:

一个字符串 A A A中的 L M S LMS LMS子串数量不超过 ∣ A ∣ / 2 |A|/2 A/2
证明:根据引理四,易证。

引理五:

对于一个后缀数组 S A SA SA,对于一个相同字母开头的所有字符串中必定先排列 L L L型,再排列 S S S型。
证明:根据引理二可得。


一些过程:

一 诱导排序通过SA1得到SA

我们如果得出 S A 1 SA1 SA1,便可以利用引理五和诱导排序得出 S A SA SA,具体过程如下:

  1. S A SA SA数组初始化为每个元素都为 − 1 -1 1的数组。
  2. 确定每个桶 S S S型桶的起始位置。在 S A 1 SA1 SA1数组中从左往右扫一遍,按照相对顺序放进对应的桶里。
  3. 确定每个桶的 L L L型后缀相对顺序,在 S A SA SA中从左往右扫,若 S A [ i ] &gt; 0 SA[i]&gt;0 SA[i]>0,且 t [ S A [ i ] − 1 ] = L t[SA[i]-1]=L t[SA[i]1]=L,则将 S A [ i ] − 1 SA[i]-1 SA[i]1塞进对应的桶里。
  4. 确定每个桶的S型后缀相对顺序,在 S A SA SA中从右往左扫,若 S A [ i ] &gt; 0 SA[i]&gt;0 SA[i]>0,且 t [ S A [ i ] − 1 ] = S t[SA[i]-1]=S t[SA[i]1]=S,则将 S A [ i ] − 1 SA[i]-1 SA[i]1塞进对应的桶里。

注意:这里的桶是合起来形成一个大桶的,即 S A SA SA,类似于基数排序的思想,提前算好每个桶的大小,故不会互相影响。
对应的桶指的是按首字母的编号形成的桶。
因为我们要确定L型后缀的起始位置,所以 S S S型桶是倒着放置的。

二 如何得到SA1

我们如果先对 L M S LMS LMS字串进行排序。
就可以得到每个 L M S LMS LMS子串的新序号。
若每个字符都不一样,则可直接得到 S A 1 SA1 SA1
否则递归求 S A 1 SA1 SA1

二 对LMS子串进行排序

我们仍然是使用诱导排序,但是要将第二步改为:
确定每个桶 S S S型桶的起始位置。将 L M S LMS LMS首字母按任意顺序放进对应的桶里。
这里我们引入一个概念 L M S LMS LMS的前缀函数。
在字符串 A A A中,如 p r e ( A , i ) pre(A,i) pre(A,i),若 t [ i ] = S , t [ i − 1 ] = T t[i]=S,t[i-1]=T t[i]=S,t[i1]=T,则 p r e ( A , i ) = A [ i ] pre(A,i)=A[i] pre(A,i)=A[i]
即它的 L M S LMS LMS前缀就是自己,否则是从 S [ i ] S[i] S[i]开始到下一个 ∗ * 型形成的字符串。
为什么按照随意顺序放可以使所有的前缀函数排序成功?
证明:

  1. 对于第二步,由于放入的 L M S LMS LMS前缀都只有一个字符,因为桶的排列是按照字典序的,所以保证放置后一定有序。
  2. 对于第三步,当放入第一个 L L L L M S LMS LMS前缀时, S A SA SA数组必定是有序的(根据引理二)。假设我们已经放置了 k k k L L L L M S LMS LMS前缀,且它们在 S A SA SA数组中保持有序,现在考虑放入的第 k + 1 k+1 k+1 L M S LMS LMS前缀是否会保证有序。我们设这个 L M S LMS LMS前缀为 p r e ( S , i ) pre(S,i) pre(S,i),因为首字母不同的 L M S LMS LMS前缀一定是保持有序的,因此我们只需要考虑它与其首字母相同的 L M S LMS LMS前缀之间的关系。假设我们存在一个这样的 L M S LMS LMS前缀,使得 p r e ( S , j ) &gt; p r e ( S , i ) pre(S,j)&gt;pre(S,i) pre(S,j)>pre(S,i),由于 p r e ( S , j ) [ 0 ] = p r e ( S , i ) [ 0 ] pre(S,j)[0]=pre(S,i)[0] pre(S,j)[0]=pre(S,i)[0],所以我们得知 p r e ( S , j + 1 ) &gt; p r e ( S , i + 1 ) pre(S,j+1)&gt;pre(S,i+1) pre(S,j+1)>pre(S,i+1)。而 p r e ( S , j + 1 ) pre(S,j+1) pre(S,j+1) p r e ( S , i + 1 ) pre(S,i+1) pre(S,i+1)都是之前所加入过的,因此它们之间应当保持有序。而 p r e ( S , j ) &gt; p r e ( S , i ) pre(S,j)&gt;pre(S,i) pre(S,j)>pre(S,i)告诉我们之前的 S A SA SA数组不是有序的,与假设相反,故不存在这样的 p r e ( S , j ) pre(S,j) pre(S,j)。因此,当 p r e ( S , i ) pre(S,i) pre(S,i)放置后,,对于所有之前放置的且首字母与其相同的 L M S LMS LMS前缀 p r e ( S , j ) pre(S,j) pre(S,j),应该都有 p r e ( S , j ) &lt; p r e ( S , i ) pre(S,j)&lt;pre(S,i) pre(S,j)<pre(S,i) S A SA SA数组保持有序。
  3. 对于第四步的正确性的证明,与第三步的证明是类似的。读者可以自行推理一下。

时间复杂度

根据引理四可知,每次递归都会将长度缩小一半。
时间复杂度为:O(n)。

主要参考:https://riteme.github.io/blog/2016-6-19/sais.html#fn:star-in-S

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值