说起来学了很久的后缀数组了
思想还是很容易明白的
最大的问题就是
代码看不懂
然后在不断模拟研究的过程中
终于弄清楚了一点
就决定写下来了
不然又会忘的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 }