• 后缀数组


    参考:https://cp-algorithms.com/string/suffix-array.html

    后缀数组指把一个字符串的所有后缀按照字典序排序得到的数组。

    以下默认下标从 (0) 开始,令 S[i...] 为字符串从 i 开始的后缀。

    sa[i]: 第 (i) 大的后缀的起始位置。

    例:ababc 的后缀从小到大排序为:空后缀,ababc,abc,babc,bc,c,其后缀数组为 [5, 0, 2, 1, 3]。

    lcp[i]:后缀数组中相邻两个后缀的最长公共前缀组成的数组,S[sa[i]...] 和 S[sa[i+1]...] 的最长公共前缀为 lcp[i]。

    sa 的计算

    倍增计算,复杂度 (O(n lg^2 n))(O(n lg n)) (优化排序部分)。

    const int MN = 400010;
    int n, k;
    int rsa[MN], tmp[MN];
    
    bool cmp(int x, int y) {
        if (rsa[x] != rsa[y]) return rsa[x] < rsa[y];
        int rx = x + k <= n ? rsa[x + k] : -1;
        int ry = y + k <= n ? rsa[y + k] : -1;
        return rx < ry;
    }
    
    template <class S> vector<int> exec(const S &s) {
        n = int(s.size());
        vector<int> sa(n + 1);
        for (int i = 0; i <= n; i++) {
            sa[i] = i;
            rsa[i] = i < n ? s[i] : -1;
        }
        for (k = 1; k <= n; k *= 2) {
            sort(sa.begin(), sa.end(), cmp); // 用计数排序可以优化这部分
            tmp[sa[0]] = 0;
            for (int i = 1; i <= n; i++) {
                tmp[sa[i]] = tmp[sa[i - 1]] + int(cmp(sa[i - 1], sa[i]));
            }
            copy(tmp, tmp + n + 1, rsa);
        }
        return sa;
    }
    

    lcp 的计算

    令 rank[i] 为后缀 i 在后缀数组的排名。

    从位置为 (0) 的后缀开始从前往后依次计算 S[sa[rank[i]-1]...] 与 S[i...] 的 lcp。

    计 k=sa[rank[i]-1],假设已经求得了 S[k...] 和 S[i...] 的 lcp 为 L,那么 S[k+1...] 和 S[i+1...] 的 lcp 一定不小于 L-1,而 S[sa[rank[i+1]-1]...] 与 S[i+1...] 的 lcp 应该还要比这个更长,于是可以用一个类似双指针的方法 (O(n)) 计算所有 lcp[i]。

    lcp 数组可以与 Sparse Table 配合使用,要计算后缀数组内任意两个串 i 和 j 的最长公共前缀,只需要计算 lcp[rank[i]],lcp[rank[i]+1],...,lcp[rank[j]-1] 的最小值。

  • 相关阅读:
    服务器/服务器架构/阿里云服务器/虚拟机
    第十五章、线程之协程
    第十五章、线程池和进程池
    第十五章、线程之queue模块的各种队列
    第十五章、Python多线程之信号量和GIL
    第十五章、Python多线程同步锁,死锁和递归锁
    第十五章、并发编程之守护线程
    第十五章、并发编程之线程
    抢票小程序
    队列与生产者消费者模型
  • 原文地址:https://www.cnblogs.com/hfccccccccccccc/p/10218160.html
Copyright © 2020-2023  润新知