【后缀数组】关于后缀数组模板的注解续

int wa[N], wb[N], ws[N], wv[N]
int rank[N], height[N]

#此处N比输入的N要多1,为人工添加的一个字符,用于避免CMP时越界
void getSA(int *r, int *sa, int n, int m) 

    int i, j, p, *x = wa, *y = wb, *t

    # bucket清空
    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)

        #string[n - j .. n] 是没有偏移为j的后缀 故第二关键字默认为0
        #所以按照第二关键字排序肯定在最前面 同时还需要保证稳定性
        for(p = 0, i = n - j; i < n; i++) y[p++] = i

        #SA定义为‘排第几的是谁‘ 根据第二关键字排序时
        #第二关键字的次序就是其关联的第一关键字未完成排序时的次序
        #所以SA[i] – j为按照第二关键字次序收集的未完成排序 的第一关键字序列
        for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] – j

        #这里的x是临时的RANK数组 y是等待排序的第一关键字下标数组(即第i后缀)
        #映射出第一关键字之间的相对大小
        for(i = 0; i < n; i++) wv[i] = x[y[i]]

        #bucket清空
        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]

        #交换x y重标号临时SA
        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++

void getHeight(int *r, int *sa, int n)

    int i, j, k = 0

    #根据SA求RANK
    for(i = 1; i <= n; i++) rank[sa[i]] = i

    #利用性质 h[i] = height[rank[i]] 减少运算时间
    #h[i] => height[rank[i]] => 表示第i后缀的height值
    #第i后缀必然比第i-1后缀短 同时注意第i后缀 = 第i-1后缀剔除首字母
    #记符号S(i)为原字符串的第i后缀
    #记符号P(i)为第i后缀的SA值-1对应的后缀
    #h[i]   = height[rank[i]]   => 第i后缀和第i后缀的SA值-1对应的后缀的LCP
    #h[i-1] = height[rank[i-1]] => 第i-1后缀和第i-1后缀的SA值-1对应的后缀的LCP
    #h[i] = LCP(S(i), P(i)) h[i - 1] = LCP(S(i - 1), P(i - 1))
    #容易看出S(i - 1)剔除首字母后变为S(i)
    #若h[i - 1] >= 1 则S(i - 1)和P(i - 1)的LCP >= 1
    #P(i - 1)肯定是某个后缀 同时因为h[i - 1] >= 1 故strlen(P(i - 1)) >= 1
    #所以P(i - 1)剔除首字母必然可以得到某个后缀
    #同时由S(i - 1)剔除首字母可以得到S(i)和P(i)
    #如果下标从1开始
    #那么P(i - 1)和P(i)必然满足P(i - 1)[1 .. h[i - 1]] == P(i)[0 .. h[i - 1]]
    #但P(i - 1)[h[i - 1] + 1 .. -1] 和 P(i)[h[i - 1] + 1 .. -1]不一定相等
    #所以h[i] >= h[i - 1] - 1
    #反之从0求h[i] 必然正确
    #补充 height[i] = LCP(string[sa[i - 1] .. -1], string[sa[i] .. -1])
    for(i = 0; i < n; height[rank[i++]] = k)
        for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++)

char str[N]
int sa[N]

int main
    char str[N]
    scanf("%s", str)
    int n = strlen(str)
    str[n] = 0

    #注意区分此处为n+1,因为添加了一个结尾字符用于区别比较
    getSA(str, sa, n + 1, 128)
    getHeight(str, sa, n)
时间: 2024-10-13 13:22:36

【后缀数组】关于后缀数组模板的注解续的相关文章

利用后缀数组构造后缀树

由于蒟蒻azui前段时间忙着准备省选,并在省选中闷声滚大粗,博客停更了好久.. 省选过后整个人各种颓,整天玩玩泥巴什么的... 前段时间学后缀数组的时候上网查相关资料,看到说后缀数组和后缀树是可以相互转化的,并且uoj上有大量通过后缀自动机建出后缀树然后dfs遍历获得后缀数组的模板,但是通过后缀数组来建后缀树的资料确实稀缺. 也许大牛们都觉得这xjbYY一下就可以写了,所以网上没找到对应的代码,那么我来补个坑吧.大牛勿喷.. 先谈谈我的理解吧.. 讲道理后缀数组和后缀树应该是完全等价的,但前两者

poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机

http://poj.org/problem?id=2774 想用后缀数组的看这里:http://blog.csdn.net/u011026968/article/details/22801015 本文主要讲下怎么hash去找 开始的时候写的是O(n^2 logn)算法 果断超时...虽然也用了二分的,, 代码如下: //hash+二分 #include <cstdio> #include <cstring> #include <algorithm> #include

使用后缀自动机求后缀数组

倒序建立后缀自动机的fail树就是后缀树,dfs后缀树得到后缀数组 #include <bits/stdc++.h> using namespace std; int last,dis[200001],val[200001],cnt,a[200001][26],fa[200001],sa[200001],n,sta[200001],top; int tr[200001][26],rank[200001],height[200001]; char st[200001]; void ins(int

bzoj2754 [SCOI2012]喵星球上的点名 (后缀数组+树状数组)

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 2745 Solved: 1190 [Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么

[TJOI2015]弦论(后缀数组or后缀自动机)

解法一:后缀数组 听说后缀数组解第k小本质不同的子串是一个经典问题. 把后缀排好序后第i个串的本质不同的串的贡献就是\(n-sa[i]+1-LCP(i,i-1)\)然后我们累加这个贡献,看到哪一个串的时候,这个贡献的和大于等于k,然后答案就在这个串里了,然后枚举就行了. 那么第k小子串该怎么办? 我们考虑二分答案,我们按字典序大小二分一个子串(具体就是二分第k小的本质不同子串,因为这个串可以\(O(n)\)求),然后看看比这个串小的串有多少个?然后改变上下界就行了. 那么我们如何求出比一个串小的

poj 3486 A Simple Problem with Integers(树状数组第三种模板改段求段)

1 /* 2 树状数组第三种模板(改段求段)不解释! 不明白的点这里:here! 3 */ 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<algorithm> 8 #define N 100005 9 using namespace std; 10 11 typedef long long LL; 12 13 LL ss[N], B[N], C[N]; 14 15

【Luogu3804】【模板】后缀自动机(后缀自动机)

[Luogu3804][模板]后缀自动机(后缀自动机) 题面 洛谷 题解 一个串的出现次数等于\(right/endpos\)集合的大小 而这个集合的大小等于所有\(parent\)树上儿子的大小 这样子的话,给每个终止位置的\(size\)记为\(1\) 然后按照拓扑序累加,这就是\(right/endpos\)集合的大小 最后对于每个\(size>1\)的节点,\(ans=max(longest*size)\) #include<iostream> #include<cstdi

队列的三种实现(静态数组、动态数组及指针)

本文有关栈的介绍部分参考自网站数据结构. 1. 队列  1.1 队列的定义 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表. (1)允许删除的一端称为队头(Front). (2)允许插入的一端称为队尾(Rear). (3)当队列中没有元素时称为空队列. (4)队列亦称作先进先出(First In First Out)的线性表,简称为FIFO表.    队列的修改是依先进先出的原则进行的.新来的成员总是加入队尾(即不允许"加塞"),每次离开的成员总是队列头

Java程序猿学习C++之数组和动态数组

数组: #include <iostream> using namespace std; //模板函数 template <class T> void dump(T val) { cout << ">>>>" << __FUNCTION__ << endl;//内置的宏,打印当前函数的名字 cout << sizeof(val) << ":" <<