后缀数组倍增法理解

int wa[maxn],wb[maxn],wv[maxn],ws[maxn];

int cmp(int *r , int a, int b, int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}
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 <<= 1,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;
        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++;
    }
}

后缀数组最终得到的是SA和RANK两个数组,SA[i]是排第i名的是谁,RANK[i]是以第i个字符为开头的后缀排第几,由于任意后缀都有确定的大小,所以SA是严格由小到大排列的,也就是说任意i都有SA[i]<SA[i+1]

然后就说一下用到的数组,x数组就是未完成的Rank数组,y数组则是一个比较所用的伪"SA"数组,ws数组则是RANK的前缀和数组。初始化的话y[i]=n-i,因为初试的时候默认的话肯定是以长度为辅排序,那么y[0]也就是说最小的辅开头就是最后一个了,然后遍历的时候其实是从y[n-1]也就是0开始的,所以原代码中写的不是很合理。

wv数组是没有必要的。

先说一下关键字的问题,初始第一关键字是s[i],第二关键字则是其下标i,然后进入到for循环中的第一关键字是s[i,i+2^k-1],第二关键字是s[i+2^k,i+2^(k+1)-1].

sa[--ws[wv[i]]] = y[i];这是排序的关键语句,对于以第一排序关键字相同的情况下,那么就是第二关键字越大的越在后面了,为了保证这一点,就要从y[n-1]到y[0]遍历。

这是用的桶排序方法,也就是说第一关键字相同的一定是在一个连续的且与其余不相交的区域内,可以结合这预处理那一段进行理解, 由于在空间上并不是连续分布的,所以这段看着会有些难懂

原文地址:https://www.cnblogs.com/mfys/p/8436470.html

时间: 2024-08-04 09:20:25

后缀数组倍增法理解的相关文章

后缀数组-倍增法

额,写的有点混乱,改天整理一下 链接:https://hihocoder.com/problemset/problem/1403 1 //字符串从1开始 2 //rank[i]为字符串中i位置起的后缀排序后的顺序 3 //sa[i]为排序后的第i位置对应的后缀在字符串中的起始位置 4 //sa[rank[i]]=i此公式在最终结果才成立 5 //height[i]为排序数组中i位置与i-1位置的最长公共前缀的长度 6 //H[i]为字符串i位置起的后缀与其在排序后前面相邻位置处的后缀的最大公共前

后缀数组模板(理解)

字符串的处理真可谓是博大精深,后缀数组这种数据结构我花了两天时间才明白了其构造的过程.主要是代码不好理解. 数据结构: 1.sa数组,就是后缀数组,按照字典序排列,其意义为:sa[i]=k,排第i名的子串是从k位开始的. 2.rank名次数组,其意义为:rank[i]=k,以i为起点的子串排名为k. 很容易看出来两者可以相互转化. 求这两个数组的过程是基于基数排序,计数排序的方法. 下面是一个大牛的注释版.其中我在补充点: 1.对于求y[]这个数组,因为已经知道上次长为j的子串比较结果,那么这次

后缀数组-倍增算法模板

void build_sa(int m) { int *x = t, *y = t2; for(int i = 0; i < m; i ++) c[i] = 0; for(int i = 0; i < n; i ++) c[x[i]=s[i]] ++; for(int i = 1; i < m; i ++) c[i] += c[i-1]; for(int i = n-1; i >= 0; i --) sa[--c[x[i]]] = i; for(int k = 1; k <=

后缀数组Da模板+注释 以及 dc3模板

后缀数组Da模板: 1 /* 2 后缀数组倍增法Da板子 3 */ 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <algorithm> 8 using namespace std; 9 const int N = 200000+9; 10 int c[N]; 11 int rank[N], height[N]; 12 int sa[N],s[N],n; 13

后缀数组(理解)

一.基本概念   后缀:用  suff[i]  表示,是指从某个位置 i 开始到整个串末尾结束的一个子串. 后缀数组:用 sa[i] 表示,是指所有后缀在排完序后,排名为i的后缀在原串中的位置.   sa[排名]=位置 名次数组:用 rank[i] 表示,是指所有后缀在排序完后,原字符串中第i个后缀现在的排名.   rank[位置]=排名 比如字符串aabaaaab$,(我们习惯在字符串后面加一个特殊字符$,表示字符串的结尾)他的所有后缀.位置.排名如下: 后缀 位置 排名 suff[ 1 ]

后缀数组之倍增算法

首先说明 :后缀数组的构建在网上有多种方法:朴素的n*n*logn,还有倍增n*logn的,还有3*n的DC3算法,当然还有DC算法.这个算法学习自林厚丛老师的<高级数据结构>,代码较长,而且常数也比较大,但是是我这种笨人可以理解的.如有人想学短而快的可以学习<罗穗骞 后缀数组 ---处理字符串的有力工具>.顺便说一下,罗大神的算法书写的的确很短小也漂亮,可惜我看不懂. 说一下学习的心路历程吧!最开始想学后缀树,道理看明的了,可是一看代码实在是太长了(可能是我找的模版不对吧).后来

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

bnuoj 34990(后缀数组 或 hash+二分)

后缀数组倍增算法超时,听说用3DC可以勉强过,不愿写了,直接用hash+二分求出log(n)的时间查询两个字符串之间的任意两个位置的最长前缀. 我自己在想hash的时候一直在考虑hash成数值时MOD取多大,如果取10^18的话,那么两数相乘个就超LL了,但是取10^9的话又怕出现重复的可能大.后面才发现自己是sb,如果用unsigned long long 如果有溢出或者为负数是直接变成对(1<<64)取模了. 也就是无符号长整形运算自动帮你取模了.所以可以放心用hash Justice S

用倍增法构造后缀数组中的SA及RANK数组

感觉后缀数组很难学的说= = 不过总算是啃下来了 首先 我们需要理解一下倍增法构造的原理 设原串的长度为n 对于每个子串 我们将它用'\0'补成长度为2^k的串(2^k-1<n<=2^k) 比如串aba的子串就有 aba'\0'    ba'\0''\0'  a'\0''\0''\0' 每次操作我们可以排出所有长度为 2^x的子串的大小 比如串aba的排序过程 第一遍 a                   a             b 第二遍 a'\0'             ab