后缀数组代码详解

Posted cminus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组代码详解相关的知识,希望对你有一定的参考价值。

说起来学了很久的后缀数组了

思想还是很容易明白的

最大的问题就是

代码看不懂

技术分享图片

然后在不断模拟研究的过程中

终于弄清楚了一点

就决定写下来了

不然又会忘的QAQ

以下是代码

ps:首先要弄懂基数排序

 

定义:c[ ]数组 : 基数排序的桶

           x[ ]数组:类似于rank数组,保存当前排序到的以每个位置开始的一段字符串的排名

           sa[ ]数组:sa[i]保存第i个排名的字符串开头所在位置

           y[ ]数组:第二关键字排序,y[i]保存第i个排名的字符串开头所在位置

 1  void BuildSa(int m) { 
 2       int *x = t, *y = t2;       //这是对单个字符的排序
 3       for (int i = 0; i < m; i++)    c[i] = 0; // 清空c数组
 4       for (int i = 0; i < n; i++)    c[x[i] = s[i]]++; // 初始长度为1时, x[i] == s[i]
 5       for (int i = 1; i < m; i++)    c[i] += c[i - 1]; // 得到每个所在位置
 6       for (int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; // 排名为c[x[i]]的字符,是从i位置开始的,每记录一次排名就-1,到前一个位置,保证排名和对应位置不重复
 7       for (int k = 1; k <= n; k <<= 1) {
 8         int p = 0; // 计数用的
 9         for (int i = n - k; i < n; i++)    y[p++] = i;  // 0是最小的,先把后面需要补0的放在最前面
10         for (int i = 0; i < n; i++)    if (sa[i] >= k)    y[p++] = sa[i] - k; // sa[i] < k时不是第二关键字, sa[i]的排名是依次递增的,所以直接读入y数组,减去前面不是第二关键字的部分
11         for (int i = 0; i < m; i++) c[i] = 0; // 清空c数组
12         for (int i = 0; i < n; i++)    c[x[y[i]]]++; // 此时在y中已经按第二关键字排好序,x中存的是第一关键字,再直接基数排序第一关键字即可
13          for (int i = 1; i < m; i++)    c[i] += c[i - 1];
14          for (int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
15          swap(x, y);
16         // 离散化排名      
17           p = 1; x[sa[0]] = 0;
18           for (int i = 1; i < n; i++)
19             x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++; // 两个关键字都相同则排名相同
20          if (p >= n)    break; // p代表了不同排名数量,若数量已经达到n,则排序完毕,可直接退出
21          m = p;
22      }
23  }

这是得到sa数组的做法

然而

一个sa数组并没有什么用

下面是得到height数组的算法

具体证明可以参考

https://wenku.baidu.com/view/ed1be61e10a6f524ccbf85fd.html

这个论文

写的很好啦

就是没有注释...

首先还是上代码

定义:rank[ ]数组:与sa[ ]数组正好相反,rank[i] = j代表以i开始的后缀的排名

 1 void GetHeight() {
 2     int k = 0;
 3     for (int i = 0; i < n; i++)    rank[sa[i]] = i; 
 4     for (int i = 0; i < n; i++)    {
 5         if (k)    k--; // 因为h[i] >= h[i - 1] - 1(h[i]看论文的定义),所以k需要-1 
 6         int j = sa[rank[i] - 1]; //得到排名前一位后缀开始位置
 7         while (s[i + k] == s[j + k])    k++; //从后面k个开始比较,直到不相同为止
 8         height[rank[i]] = k;
 9     }
10 }

 

以上是关于后缀数组代码详解的主要内容,如果未能解决你的问题,请参考以下文章

字符串-后缀树和后缀数组详解

字符串-后缀树和后缀数组详解

后缀自动机多图详解(代码实现)

后缀数组 模板+详解

VSCode自定义代码片段—— 数组的响应式方法

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段