[对罗穗骞论文的一点理解]后缀数组——倍增算法
来源:互联网 发布:浪潮软件(600756)股吧 编辑:程序博客网 时间:2024/06/06 13:17
基本定义
首先先来看几个定义:
字符串的大小比较
从第一位开始,按照字典序比较大小,如果相同则比较后一位,否则按照当前这一位的比较结果确定结果,如果比较的时候这一位已经超出其中一个串的长度,则比较它们的长度,长的一个串比较大,如果长度相等,那么它们也相等。
后缀数组
用
名次数组
用
简单地说,后缀数组存的就是排第
性质:
在求出了
同样,如果求出了这两个数组的任意一个,就可以利用
设一个字符串的长度为
倍增算法的实现
主要思路:
利用倍增的思想,对每一个字符开始的长度为
这时,
从0开始,每一次
利用这样类似RMQ-ST的做法,就可以求出最终的
以下借用一张罗穗骞神犇论文中的一张图,来表示倍增算法运行的过程。
那么,对于排序来说,应该用什么方法呢?在论文中,提到了一种叫做基数排序的神奇的排序方法。对于这个算法,理论上是可以只跑
来看一看代码
void da(int *r, int *sa, int n, int m) { int i, j, p, *x = wa, *y = wb, *t; for(i = 0; i < m; i++) { ws[i] = 0; // 初始化 } for(i = 0; i < n; i++) { ws[x[i] = r[i]]++; } for(i = 1; i < m; i++) { ws[i] += ws[i - 1]; } for(i = n - 1; i >= 0; i--) { sa[--ws[x[i]]] = i; }//基数排序 for(j = 1, p = 1; p < n; j *= 2, m = p) { //倍增 for(p = 0, i = n - j; i < n; i++) { y[p++] = i; } for(i = 0; i < n; i++) { if(sa[i] >= j) y[p++] = sa[i] - j; } // 排序第二关键字 借用之前的sa for(i = 0; i < n; i++) { wv[i] = x[y[i]]; } for(i = 0; i < m; i++) { ws[i] = 0; } for(i = 0; i < n; i++) { ws[wv[i]]++; } for(i = 1; i < m; i++) { ws[i] += ws[i - 1]; } for(i = n - 1; i >= 0; i--) { sa[--ws[wv[i]]] = y[i]; } // 基数排序 排序第一关键字 for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } return;}代码有点长,因为是自己重打一遍的原因,而且代码风格比较不一样,有很多空行,实际上真正有意义的也就只有大概30行左右待排序的字符串存在
for(i = 0; i < m; i++) { ws[i] = 0; }for(i = 0; i < n; i++) { ws[x[i] = r[i]]++;}for(i = 1; i < m; i++) { ws[i] += ws[i - 1];}for(i = n - 1; i >= 0; i--) { sa[--ws[x[i]]] = i;}
这里的
接下来要进行若干次基数排序,在这里有一个优化。
基数排序的过程分为两次,第一次是对第一关键字排序,第二次是对第二关键字排序。对于第二次排序,其实可以用上一次的求出的
for(p = 0, i = n - j; i < n; i++) { y[p++] = i;}for(i = 0; i < n; i++) { if(sa[i] >= j) y[p++] = sa[i] - j;}
这里的j是字符串的长度,数组y保存的则是对第二关键字排序的结果,接着就要对第一关键字进行排序。
for(i = 0; i < n; i++) { wv[i] = x[y[i]]; } for(i = 0; i < m; i++) { ws[i] = 0; } for(i = 0; i < n; i++) { ws[wv[i]]++; } for(i = 1; i < m; i++) { ws[i] += ws[i - 1]; } for(i = n - 1; i >= 0; i--) { sa[--ws[wv[i]]] = y[i]; }
求出新的
求
这里有两个在空间和时间上的优化:
因为
for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;}
其中
int cmp(int *r, int a, int b, int l) { return r[a] == r[b] && r[a + l] == r[b + l];}
可以先模拟一遍,这样就更容易理解原文中的一段话。
如果
执行完所有以上代码之后,在这时,
这里可以加上一个优化,如果
那么,循环的初始化和结束就写成了一开始的代码那样,这里再看一次。
for(j = 1, p = 1; p < n; j *= 2, m = p) {......}
最后再来看看时间复杂度,倍增部分最多执行
原论文中还提到了一种叫做dc3的算法,时间复杂度仅为
- [对罗穗骞论文的一点理解]后缀数组——倍增算法
- 后缀数组——倍增算法
- 后缀数组——罗穗骞倍增算法详细注释
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组——罗穗骞倍增算法详细注释
- 后缀数组——罗穗骞倍增算法详细注释
- 后缀数组学习笔记——罗穗骞倍增算法代码
- 后缀数组——罗穗骞倍增算法代码详解
- 后缀数组 倍增算法
- 后缀数组,倍增算法
- 倍增算法求解字符串的后缀数组
- 倍增算法实现后缀数组的构造
- 利用倍增算法的后缀数组
- 后缀数组之倍增算法——学习笔记
- 倍增算法实现后缀数组
- 后缀数组之倍增算法
- 我的Python路——day one
- iphone尺寸
- LinkedList源代码的分析
- Linux开启ssh服务
- ubuntu下tomcat安装配置及idea下建立jps工程中遇到的小问题
- [对罗穗骞论文的一点理解]后缀数组——倍增算法
- 好友推荐算法-基于关系的推荐
- Git问题Everything up-to-date解决
- 使用Shell编写定时向指定API获取数据的脚本
- HDU 4333 Revolving Digits [扩展KMP]
- 文本首行缩进
- 【58】对称的二叉树
- 旅行社之家(www.lxshome.com)正式上线
- AlertDialog 警告对话框