后缀数组倍增构造算法说解
来源:互联网 发布:淘宝几星可以开直通车 编辑:程序博客网 时间:2024/05/17 08:46
后缀数组,作为一种高效的字符串处理数据结构,没有哈希算法的随机性,也没有 KMP 算法的局限性,是作为一种较为全面而编程思想不甚复杂的数据结构加以应用。后缀数组应用最为广泛的一种构建方法是倍增构造,时间复杂度为 O(nlogn),其中 n 为待处理字符串的长度。
我们不妨设待处理的字符串储存在 s[1 .. n] 中,则在一个后缀数组 sa[1 .. n] 中,sa[i] 表示该字符串的所有后缀集合 {s[1 .. n], s[2 .. n], .. , s[n .. n]} 中字典序为 i 的后缀为 s[sa[i] .. n]。与之相对应的后缀名次数组 rank[1 .. n] 中,rank[i] 表示后缀 s[i .. n] 在该字符串的所有后缀集合 {s[1 .. n], s[2 .. n], .. , s[n .. n]} 中的字典序排名为 rank[i]。显然,rank[sa[i]] = sa[rank[i]] = i。
另外,为了效率更高地解决字符串问题,后缀数组通常有一个辅助数组 height[1 .. n],height[i] 表示后缀 s[sa[i-1] .. n] 和后缀 s[sa[i] .. n] 的最长公共前缀的长度。由于存在性质:height[rank[i]] ≥ height[rank[i - 1]] - 1,故在求出 sa[1 .. n] 后 height[1 .. n] 可以在线性时间复杂度内求出。
倍增算法究其本质,类似于归并排序的过程。当第 i 轮倍增结束后,算法保证原字符串的所有后缀已经按前
对于以上过程成立的原因,我们可以如此考虑。rank[i][j] 表示 s[j .. j +
在每一轮倍增过程的排序中,若使用快排,则每轮倍增的时间复杂度为 O(nlogn),整个倍增过程的时间复杂度为 O(
每一轮基数排序的过程中,都会进行两次计数排序。第一次计数排序,以第二关键字对当前名次数组排序,将排序结果存储在索引数组里。第二次计数排序,以索引数组为遍历顺序,以第一关键字对当前名次数组排序,将排序结果存储在当前后缀数组里。这样可以保证基数排序的正确性。计数排序的代码如下:
#define CountSort(_rank,_sa,_index,_n,_m,_incr) \{ \ for(int __tmp_i=0;__tmp_i<=_m;__tmp_i++) \ count[__tmp_i]=0; \ for(int __tmp_i=1;__tmp_i<=_n;__tmp_i++) \ count[__tmp_i+_incr<=_n?_rank[__tmp_i+_incr]:0]++; \ for(int __tmp_i=1;__tmp_i<=_m;__tmp_i++) \ count[__tmp_i]+=count[__tmp_i-1]; \ for(int __tmp_i=_n;__tmp_i>0;__tmp_i--) \ _sa[count[__tmp_i+_incr<=_n? \ _rank[_incr>0?__tmp_i+_incr:_index[__tmp_i]]:0]--] \ =_incr>0?__tmp_i:_index[__tmp_i]; \}
基数排序后,要对名次数组重编号,其中第一关键字和第二关键字对应相同的元素,名次也应相同。由于基数排序处理出当前后缀数组,所以可以用当前后缀数组将当前名次数组处理出来。具体方法是,必然有 rank[sa[i - 1]] = rank[sa[i]] 或 rank[sa[i - 1]] + 1 = rank[sa[i]]。重编号的代码如下:
#define ReOrder(_rank,_sa,_index,_n,_m,_incr) \{ \ for(int __tmp_i=1;__tmp_i<=n;__tmp_i++) \ _index[__tmp_i]=_rank[__tmp_i]; \ _rank[_sa[1]]=1; \ for(int __tmp_i=2;__tmp_i<=n;__tmp_i++) \ if(_index[_sa[__tmp_i-1]]!=_index[_sa[__tmp_i]] \ ||(_sa[__tmp_i-1]+_incr<=_n)!=(_sa[__tmp_i]+_incr<=_n) \ ||_sa[__tmp_i]+_incr<=_n \ &&_index[_sa[__tmp_i-1]+_incr]!=_index[_sa[__tmp_i]+_incr]) \ _rank[_sa[__tmp_i]]=_rank[_sa[__tmp_i-1]]+1; \ else \ _rank[_sa[__tmp_i]]=_rank[_sa[__tmp_i-1]]; \ _m=_rank[_sa[_n]]; \}
在倍增算法之前,先将字符转成数字存储在名次数组里,再用一次重编号将 rank[0][1 .. n] 处理出来。在倍增算法的过程中,若增量 i 大于等于 n 或重编号后的最大名次等于 n,则可以确定后缀数组已经构造完成。倍增算法的代码如下:
char s[MAX_N];int height[MAX_N],count[MAX_N],n,rank[MAX_N],sa[MAX_N];void Doubling(){ int m; for(int i=1;i<=n;i++) height[i]=i, rank[i]=s[i]-'a'+1; CountSort(rank,sa,height,n,26,0); ReOrder(rank,sa,height,n,m,0); for(int i=1;i<n&&m<n;i<<=1) { CountSort(rank,height,sa,n,m,i); CountSort(rank,sa,height,n,m,0); ReOrder(rank,sa,height,n,m,i); } m=0; for(int i=1;i<=n;height[rank[i++]]=m) for(m?m--:0;s[i+m]==s[sa[rank[i]-1]+m];m++);}
- 后缀数组倍增构造算法说解
- 后缀数组 (由倍增算法构造)
- 倍增算法实现后缀数组的构造
- 2倍倍增算法构造后缀数组
- 后缀数组 倍增算法
- 后缀数组,倍增算法
- 使用倍增算法(Prefix Doubling)构造后缀数组
- 后缀数组(基本概念及其构造方法之倍增算法)
- 倍增算法实现后缀数组
- 后缀数组之倍增算法
- 后缀数组倍增算法模版
- 后缀数组之倍增算法
- 后缀数组 倍增算法模板
- 后缀数组 倍增算法详解
- 倍增算法求解字符串的后缀数组
- 后缀数组,Manber & Mayer 倍增算法
- 后缀数组 倍增算法 代码详解
- 后缀数组(SA倍增算法)
- Yii2使用Redis
- 光照模型小结
- Excel函数学习——vlookup
- delphi获取本机IP地址
- CT107D蓝桥杯DS18B20使用
- 后缀数组倍增构造算法说解
- HTML学习笔记——框架
- CoreData笔记
- Andorid 中 ImageView 的不同属性 ScaleType 的区别
- 软件框架-无绪开发4
- nexus3 linux搭建
- 9*9乘法表
- Android 中使用自定义ttf字体实现酷炫效果
- Angular Module声明和获取重载