• 后缀数组


    基础结构

    后缀数组主要是有两部分共同构成,分别是 (tr[][]) 边和 (link[]) 边,

    后缀数组中,一个状态也代表一个字符串 , 注意了 ,因为后缀数组是个 DAG , 所以对于 (x) 点,认定其状态是从起点走到他自己的最长路径 (最长的字符串) ,而其他路径都是 (x) 字符串的后缀。

    (tr[x][i]) 和字典树含义一样,指的是 (x) 字符串接上 (i) 就是 (tr[x][i]) 字符串,同时顺着 (tr) 走可以走完一个字符串里所有的子串。

    (link[x]) 则代表当前后缀数组中 , (x) 字符串与其他存在于后缀数组的字符串的最长公共后缀 (也是一个字符串)。

    此外,还会记录一个 (len[x]) ,表示 (x) 状态的字符串的长度。

    构建方法

    对一个字符串构建后缀数组,总体上是将其所有的前缀从短到长依次都加入后缀数组中。

    而一步一步来看,实际上第i个前缀我们都只是把 (s[i]) 这个字符加入。

    我们假设已经正在加入 (c) 字符,上一个加入的点是 (last) ,这一次加入的点是 (nxt) ,很明显的是 (tr[last][c]=nxt)

    那么主要是找到 (link[nxt]) 了,我们设 (p=last) ,然后让 (p) 沿着 (link[p]) 往回跳:

    1. 如果 (tr[p][c]) 不存在,那么 (tr[p][c]) 可以直接连到 (nxt)

    2. 如果 (tr[p][c]) 存在,且 (len[tr[p][c]]=len[p]+1) ,则 (link[nxt]=tr[p][c]) 。终止操作。

    3. 如果 (tr[p][c]) 存在,但 (len[tr[p][c]]>len[p]+1) ,则进行下列操作:

    (q = tr[p][c]) ,新建一个 (clone) 点,完全将 (q) 点的 (link)(tr) 复制到 (clone) , 随后将 (link[q]) 指向 (clone) 并且顺着 (p) 回跳 , 把 (tr[p][c]=q) 的 改为 (tr[p][c]=clone) 。终止操作。

    可以发现,加入 (s[i]) 后,相当于加入了以 (i) 为结尾的前缀,随后 (link) 边会更新改变, (link) 边会形成一棵树,两点 LCA 为其最长公共后缀。

    未完待续

  • 相关阅读:
    C++:delete和delete[]释放内存的区别
    C++:四种必须使用初始化列表情况
    C++:获取数组长度
    C++:构造函数默认的参数声明
    java 的开源wiki维基系统
    openfire 最大连接数调优
    即时通讯服务器的对比
    分分钟带你玩转 Web Services
    让git忽略ignore所有文件,只对某些文件进行版本控制
    miracast 协议wifi display
  • 原文地址:https://www.cnblogs.com/redegg/p/12167318.html
Copyright © 2020-2023  润新知