后缀数组之倍增算法
来源:互联网 发布:mysql 自带记录行号 编辑:程序博客网 时间:2024/05/01 19:39
后缀数组是后缀树的替代品。它不仅时间效率高,而且代码比较简单,不易写错。
后缀数组可以查询模板串
我们定义:
- 后缀k:后缀s[k…n-1]
- k前缀:对于字符串u,u的前k个字符组成的字符串(k过大即为整个u)
- 后缀数组(sa[i]):对所有后缀排序之后排名第i(
0≤i<n )的后缀的编号 - 名词数组(rank[i]):后缀i的排名。
我们主要研究的是后缀数组的构造,因为文本串长度n比较大,所以我们要想办法设计出快速的算法,构造出后缀数组以后,我们只要在后缀数组中二分就可以找到是否存在一个后缀的前缀(即子串)与模板串匹配就行了(因为后缀数组是有序的),查询的时间复杂度为
倍增算法是构造后缀数组代码比较简单且容易理解一种算法。
倍增算法的主要思路是:
利用如下性质:
- 后缀i和后缀j的前2k个字符相等<=>后缀i和后缀j的前k个字符相等并且后缀i+k和后缀j+k的前k个字符相等
- 后缀i的前2k个字符小于后缀j的前2k个字符<=>(后缀i的前k个字符小于后缀j的前k个字符)或(后缀i的前k个字符等于后缀j的前k个字符且后缀i+k的前k个字符小于后缀j+k的前k个字符)
虽然描述有些冗杂,但还是很好理解的。
我们发现已知所有后缀前k个字符的大小关系就可以推导出所有后缀的前2k个字符的大小关系。
我们把后缀i的前k个字符的排名做为第一关键字,把后缀i+k的前k个字符的排名作为第二关键字,然后排序就可以求出后缀i的前2k个字符的排名。
所以用倍增的方法对每个字符开始的长度为2k的子字符串进行排序,求出排名,即rank值。k从0开始,每次加1,当2k大于n以后,每个字符开始的长度为2k的子字符串便相当于所有的后缀。并且这些子字符串都一定已经比较出大小,即rank值中没有相同的值,那么此时的rank值就是最后的结果。每一次排序都利用上次长度为2k-1的字符串的rank值,那么长度为2k的字符串就可以用两个长度为2k-1的字符串的排名作为关键字表示,然后进行基数排序,便得出了长度为2k的字符串的rank值。
对于排序,最好的选择是使用基数排序(如果不知道基数排序,最好去看看,如果不懂的话很难看懂程序),然后我们就求出了后缀数组了。。。
代码如下:
#include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int MAXN = 10000010;char s[MAXN], P[MAXN];int sa[MAXN], t[MAXN], t2[MAXN], c[MAXN], n, m;void build_sa(int m) { int k, i, *x = t, *y = t2; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[i] = s[i]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; for(k = 1; k <= n; k <<= 1) { int p = 0; for(i = n-k; i < n; i++) y[p++] = i; for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[y[i]]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i++) x[sa[i]] = y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++; if(p >= n) break; m = p; }}int cmp(char* P, int i) { return strncmp(P, s+sa[i], m);}int find(char* P) { m = strlen(P); if(cmp(P, 0) < 0) return -1; if(cmp(P, n-1) > 0) return -1; int L = 0, R = n-1; while(L <= R) { int mid = (L+R)>>1, res = cmp(P, mid); if(res == 0) return sa[mid]; if(res < 0) R = mid-1; else L = mid+1; } return -1;}int main() { int i, t; scanf("%s", s); n = strlen(s); build_sa(200); scanf("%d", &t); for(i = 1; i <= t; i++) { scanf("%s", P); printf("%d\n", find(P)); } return 0;}
其实后缀数组的代码可读性普遍不强。。。
知道原理也很难看懂。。。
请大家自己慢慢研究。。。
- 后缀数组之倍增算法
- 后缀数组之倍增算法
- 后缀数组 倍增算法
- 后缀数组,倍增算法
- 倍增算法实现后缀数组
- 后缀数组倍增算法模版
- 后缀数组 倍增算法模板
- 后缀数组 倍增算法详解
- 后缀数组之倍增法
- 后缀数组(基本概念及其构造方法之倍增算法)
- 后缀数组之倍增算法——学习笔记
- 倍增算法求解字符串的后缀数组
- 后缀数组,Manber & Mayer 倍增算法
- 后缀数组 倍增算法 代码详解
- 后缀数组 (由倍增算法构造)
- 后缀数组(SA倍增算法)
- 后缀数组2倍增算法读书笔记
- 后缀数组--学习笔记(倍增算法)
- 关于android中bitmap使用时的oom的一些解决方法
- Oracle字符函数
- 常用php函数,来自互联网
- SpringMVC与Mybatis集合实现分页功能(实际项目)
- 悬浮怪物血条功能
- 后缀数组之倍增算法
- Android各种蓝牙设备的UUID
- 20160812Object-c关于内存管理一
- 使用spring-data-redis进行对redis的操作,封装的一些操作方法
- 25.html标签居然可以写事件
- SQL Server连接Oracle详细步骤
- HDU 1257 最少拦截系统
- SVN服务器搭建和使用(二)
- Bluetooth MTU