码迷,mamicode.com
首页 > 编程语言 > 详细

后缀数组(模板)

时间:2020-07-16 21:41:48      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:image   倍增   lazy   com   示意图   turn   http   实现   ret   

介绍

后缀数组就是字符串的每个后缀的排序。
主要有两个sa和rk两个数组,sa[i]代表第i大的后缀的位置,rk[i]代表位置i的后缀的排位。满足rk[sa[i]] = sa[rk[i]] = i

实现

有很多求后缀数组的方法,其中一种是倍增法。
先给字符串每一位排序,然后倍增排序。假设当前倍增长度为\(2^k\),那么对于位置i,以rk[i]为第一关键字,rk[i+\(2^k\)]为第二关键字排序。
时间复杂度O(n(logn)^2)。

偷个oiwiki的图,倍增排序示意图:
技术图片

还有O(n)的复杂度的方法,有机会再补了。

const int N = 2e5 + 10;
int sa[N], rk[N << 1], oldrk[N << 1]; //倍增要开两倍空间,每次排序的格式化也要格式化两倍空间(重要)
char s[N];

int main() {
    IOS;
    cin >> s + 1;
    int n = strlen(s + 1);
    for(int i = 1; i <= n; i++) {
        rk[i] = s[i] - ‘a‘ + 1; //由于rk初始化为0,所以一开始要从1开始
    }
    for(int i = 1; i <= n; i++) sa[i] = i;
    for(int w = 1; w < n; w <<= 1) {
        for(int i = 1; i <= n; i++) sa[i] = i;
        sort(sa + 1, sa + n + 1, [w](int x, int y) {return rk[x] == rk[y] ? rk[x + w] < rk[y + w] : rk[x] < rk[y];});
        for(int i = 0; i <= (n << 1); i++) oldrk[i] = rk[i]; //两倍空间,拷贝要完整地拷贝
        int p = 0;
        for(int i = 1; i <= n; i++) {
            if(oldrk[sa[i]] == oldrk[sa[i - 1]] && oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
                rk[sa[i]] = p;
            } else {
                rk[sa[i]] = ++p;
            }
        }
    }
    for(int i = 1; i <= n; i++) cout << sa[i] << " "; 
    cout << endl;
}

height数组

\(height[i] = lcp(sa[i], sa[i-1])\), 即第i名的后缀与它前一名的后缀的最长公共前缀。

具体就是使用引理\(height[rk[i]] \le height[rk[i-1]]-1\)来暴力求,时间复杂度O(n)。

int k = 0;
for(int i = 1; i <= n; i++) {
    if(k) k--;
    while(i + k < n && s[i + k] == s[sa[rk[i] - 1] + k]) k++;
    ht[rk[i]] = k;
}
for(int i = 2; i <= n; i++) cout << ht[i] << " ";
cout << endl;

参考

后缀数组(模板)

标签:image   倍增   lazy   com   示意图   turn   http   实现   ret   

原文地址:https://www.cnblogs.com/limil/p/13324522.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!