后缀数组,高度数组
2016-02-15 09:20
260 查看
后缀数组:SA[i]表示字典序第i的后缀的开头字母的位置,比如abca,后缀有五个,abca,bca,ca,a,空后缀
求后缀数组的方法是……类似分治法,类似二分法?
首先求出每个后缀首字母的顺序,然后依据这个顺序求出每个后缀首两个字母开头的后缀的顺序,最后就能求出每个后缀的顺序
具体实现上,参考挑战程序设计竞赛里给的(有更好的基数排序算法)(代码没编译过……下同)
int n,k;
int rank[maxn_n + 1];
int tmp[manx_n + 1];
for(int i = 0; i <= n; i++){
sa[i] = i;
rank[i] = i < n? s[i]:-1;//直接去字符值,空的是0
}
for(k = 1; k <= n; k*=2)
{
sort(sa,sa + n + 1,compare_sa tmp[sa[0]] = 0;
for(int i = 1; i <= n; i++));//每次排序之前和本来的顺序没什么关系,只和rank数组有关系,每一个数字如果大的话,就是rank数组大
//获取新的rank数组
{
tmp[sa[i]] = tmp[sa[i - 1]] + (compare(sa[i - 1],sa[i]) ? 1:0);//要用本来的rank来compare,所以先用tmp临时存储
}
for(int i = 0; i <= n; i++)
{
rank[i] = tmp[i];
}//rank从0开始,代表每个的名次(重复的都已第一个名词计算)
bool compare_sa(int i,int j)
{
if(rank[i] != rank[j] ) return rank[i] < rank[j];
else
{
int ri = i + k <= n? rank[i + k] : -1;//如果加了k之后超过了,那就是说后面没了,就是-1
int rj = j + k <= n? rank[j+k] : -1;
return ri < rj;
}
高度数组,lcp[i]指的是后缀数组中第i个后缀和第i-1个后缀的最长公公前缀长度
利用到两个特点
性质1. 原来字符串中 S【i】和 S【rank【i】 - 1】是lcp【i】
而S【i + 1】和s【rank【i】 - 1 + 1】至少有lcp【i】 - 1长度的公共前缀和
性质2.如果S1<S2<S3那么s1和s3的公共前缀和就一定小于等于s2和s3的公共前缀和,举个例子
abca < a b d a < abda
所以说s[i + 1]和 s【rank【i+1]-1】的公共前缀和一定大于等于上述的icp【i】 -1那个值
可以从i = 0开始计算公共前缀和,利用上述条件,可以推出复杂度是O(N)
代码也是来自《挑战程序设计竞赛》
反正理解之后就套模板吧……就是可以在o(n)时间内求得第i和第i+1个后缀的lcp
求出高度数组之后,结合rmq,可以快速得到任意两个后缀的最长公共前缀,也是利用了性质2
如果rank[i] < rank[j] 那么 lcp(i,j) = min(lcp[x] i < = x < j)
求后缀数组的方法是……类似分治法,类似二分法?
首先求出每个后缀首字母的顺序,然后依据这个顺序求出每个后缀首两个字母开头的后缀的顺序,最后就能求出每个后缀的顺序
具体实现上,参考挑战程序设计竞赛里给的(有更好的基数排序算法)(代码没编译过……下同)
int n,k;
int rank[maxn_n + 1];
int tmp[manx_n + 1];
for(int i = 0; i <= n; i++){
sa[i] = i;
rank[i] = i < n? s[i]:-1;//直接去字符值,空的是0
}
for(k = 1; k <= n; k*=2)
{
sort(sa,sa + n + 1,compare_sa tmp[sa[0]] = 0;
for(int i = 1; i <= n; i++));//每次排序之前和本来的顺序没什么关系,只和rank数组有关系,每一个数字如果大的话,就是rank数组大
//获取新的rank数组
{
tmp[sa[i]] = tmp[sa[i - 1]] + (compare(sa[i - 1],sa[i]) ? 1:0);//要用本来的rank来compare,所以先用tmp临时存储
}
for(int i = 0; i <= n; i++)
{
rank[i] = tmp[i];
}//rank从0开始,代表每个的名次(重复的都已第一个名词计算)
bool compare_sa(int i,int j)
{
if(rank[i] != rank[j] ) return rank[i] < rank[j];
else
{
int ri = i + k <= n? rank[i + k] : -1;//如果加了k之后超过了,那就是说后面没了,就是-1
int rj = j + k <= n? rank[j+k] : -1;
return ri < rj;
}
高度数组,lcp[i]指的是后缀数组中第i个后缀和第i-1个后缀的最长公公前缀长度
利用到两个特点
性质1. 原来字符串中 S【i】和 S【rank【i】 - 1】是lcp【i】
而S【i + 1】和s【rank【i】 - 1 + 1】至少有lcp【i】 - 1长度的公共前缀和
性质2.如果S1<S2<S3那么s1和s3的公共前缀和就一定小于等于s2和s3的公共前缀和,举个例子
abca < a b d a < abda
所以说s[i + 1]和 s【rank【i+1]-1】的公共前缀和一定大于等于上述的icp【i】 -1那个值
可以从i = 0开始计算公共前缀和,利用上述条件,可以推出复杂度是O(N)
代码也是来自《挑战程序设计竞赛》
int rank[maxn_n+1]//表示i开头的后缀的排名 void construct_lcp(string s,int *sa,int *lcp)//传入的是后缀数组和原来的字符串 { int n = s.lenght(); for(int i = 0 ;i <= n; i++) rank[sa[i]] = i;//每一个后缀都不相等 int h = 0; lcp[0] = 0;//初始条件 for(int i = 0; i < n; i++) { int j = sa[rank[i] - 1]; if(h >0) h--; for(;j + h < n && i + h < n;h++)//第一次的时候h = 0,从头比较,之后可以利用前面的h { if(s[j + h] != s[i + h] break; } lcp[rank[i] - 1] = h; } }
反正理解之后就套模板吧……就是可以在o(n)时间内求得第i和第i+1个后缀的lcp
求出高度数组之后,结合rmq,可以快速得到任意两个后缀的最长公共前缀,也是利用了性质2
如果rank[i] < rank[j] 那么 lcp(i,j) = min(lcp[x] i < = x < j)
相关文章推荐
- 数据库链接字符串查询网站
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Flex字符串比较 还有Flex字符串操作
- Ruby实现的矩阵连乘算法
- Ruby中创建字符串的一些技巧小结
- ASP下经常用的字符串等函数参考资料
- 将字符串小写转大写并延时输出的批处理代码
- 将字符串转换成System.Drawing.Color类型的方法
- C#插入法排序算法实例分析
- Lua源码中字符串类型的实现
- Lua性能优化技巧(四):关于字符串
- 字符串聚合函数(去除重复值)
- Ruby中的字符串编写示例
- 总结的5个C#字符串操作方法分享
- sqlserver中求字符串中汉字的个数的sql语句
- sql server字符串非空判断实现方法
- VBS的字符串及日期操作相关函数
- C#实现将千分位字符串转换成数字的方法
- jquery 删除字符串最后一个字符的方法解析