后缀树组 倍增算法



这两天在学习后缀数组,但是翻阅网上很多博客都没有找到一个比较适合初学者的,于是没有办法,自己参考着-----算法合集之《后缀数组——处理字符串的有力工具》去学习,发了两天时间,虽然没有弄懂,那个倍增法的原理,但是步骤是了解了。为了省去读者的切换,我从作者那边复制我认为重要并且代码讲解需要的部分。如下

1.1基本定义

 

子串:

   字符串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)]。(也就是说后缀是特殊的子串  这是我自己加上的

大小比较:

  关于字符串的大小比较,是指通常所说的“字典顺序”比较,也就是对于两个字符串u、v,令i从1开始顺次比较u[i]和v[i],

  如果u[i]=v[i] 则令i加1,否则若u[i]<v[i]则认为u<v,u[i]>v[i]则认为u>v(也就是v<u),比较结束。如果i>len(u)或者i>len(v)仍比较不出结果,那么若
         len(u)<len(v)则认为
         u<v,若len(u)=len(v)则认为
         u=v,若
         len(u)>len(v)则u>v。
         从字符串的大小比较的定义来看,S的两个开头位置不同的后缀u和v进行比较的结果不可能是相等,因为u=v的必要条件len(u)=len(v)在这里不可能满足。

后缀数组:

  后缀数组SA是一个一维数组,它保存1..n的某个排列SA[1],SA[2],……,SA[n],并且保证Suffix(SA[i])<Suffix(SA[i+1]),1≤i<n。也就是将S的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA中。

名次数组:

   名次数组Rank[i]保存的是Suffix(i)在所有后缀中从小到大排列的“名次”。


简单的说,后缀数组是“排第几的是谁?”,名次数组是“你排第几?”。容易看出,后缀数组和名次数组为互逆运算。如图1所示。

  image

上面那句话是比较重点  因为到下面讲解时容易混淆。

 

为了完成后缀数组的学习,我们还要学习基数排序,基数排序有LSD与MSD   后缀树组只要学习LSD 为什么强调这点,因为之前看代码不懂,以为是MSD的代码,结果去学MSD,但是网上也没有比较好的MSD(我没有找到,不能说没有),为了方便这里写出LSD的排序方法(百度百科)。但是我认为这个例子足够掌握LSD了。

 

假设原来有一串数值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:

0

1 81

2 22

3 73 93 43

4 14

5 55 65

6

7

8 28

9 39

第二步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接着再进行一次分配,这次是根据十位数来分配:

0

1 14

2 22 28

3 39

4 43

5 55

6 65

7 73

8 81

9 93

第三步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

14, 22, 28, 39, 43, 55, 65, 73, 81, 93

 

如果大家还是有点不是太懂,我们这样看

 

          
          
   43      
   93 65    
   81  22731455     28  39

0    1       2       3      4      5        6          7       8       9

 

把0到9想象成一个队列,先进先出,第一次比较个位,结尾是1的  就放在1号这个队列里,依次放入然后按从小到大取出得到新的数组81, 22, 73, 93, 43, 14, 55, 65, 28, 39   对于3号队列里放了3个所以,先进先出,73最开始进去的,所以第一个拿出来,第二部,以这个最新的数组再次放入,这次按十位这个位的数字放入。

 

好了,你已经学会了基数排序,然后让我们开始着手后缀数组吧。

 

要想实现后缀数组,最重要的就在这个图上,

image

 

rank数组就是上面提到的名词数组,那么怎么求呢?

 

那个图就是步骤,让我解释一下原图的意思,第一次排序就是讲原来的字母按照大小比较生成rank数组,然后将rank数组组合成一个2位十进制

也就是11   12   21   11   11   11   11   12  20  就是下面想 x y那一栏  然后比较这些十位数的大小  又生成一个rank[]数组,然后重复,什么时候结束呢,当我们发现rank[]数组没有重复排名的时候就停止,大家,这里不要想着rank的含义,就想着他是一个普通的一位数。

 

下面就是比较核心的了,怎么生成rank[]数组,就是基数排序 

也许你会问快排不行吗,当然可以,只是这里就是俩位数,用基数排序,时间复杂度更低,为什么?快排的时间复杂度位n*logn   而基数排序的时间复杂度为O(n)    最坏情况下,排序次数为logn次,所以快排总的时间复杂度为O(nlogn*logn)。基数排序的时间复杂度为n*logn

具体证明这里就不写了,当然我在写这篇博客的时候是不会证明的,但是论文上是这样说的。

 

 

正题:

 

首先  对数组   a a b a a a a b进行排序 

 

第一步  先将第一个a加入进来

          
          
          
          
      a           

a       b       c      d      e      f        g      

 

 

第二步  先将第二个a加入进来  这里为了区分  不同a   所以用字母在数组中的下标来表示

          
          
          
      1         
      0           

a       b       c      d      e      f        g      

 

第三步  将下标为2的b加入进来 (数组下标从0开始)

          
          
          
      1         
      0   2         

a       b       c      d      e      f        g      

 

第四步  将下标为3的a加入进来 (数组下标从0开始)

          
          
      3         
      1         
      0   2         

a       b       c      d      e      f        g      

 

依次进行,这里就省略了

直接到最后一步

      6    
      5    
      4    
      3    
      1        7   
      0        2   

a                    b                  c               d                      e     

 

 

然后取出  先从小的取(小的指a b c d e 也就是分割线下面的   a b c d e)

a最小并且有数据,从a取,这么多,怎么取,先进先出,最底下的最开始放进去,那么他也最开始拿出来,依次是

0 1 3 4 5 6 2 7 (这里是拿出来的下标)

 

那么rank怎么得到呢,相必大家都看出来了,在一列的是一个等级,也就是说 (0 1 3 4 5 6)这个是相等的等级那么至于等级

从0开始表示最大还是从1开始表示最大,随便你

 

那么这里就要知道rank[]数组的含义了,rank[i]表示在原数组,也就是要排序的数组,下标为i的元素的排名,相等元素排名一样

所以得到rank[0] 代表(a a b a a a a b)第0个下标所代表的元素的排名,因为下标为0的a  在第一列中所以我设为1

rank[0] = 1

下面考察下标为2的b(a a b a a a a b)  因为下标为 2 的b在第二列中  所以我排名为2.

 

所以会得到

rank数组为{ 1 1 2 1 1 1 1 2}  如图片所示。待续。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值