基础结构
后缀数组主要是有两部分共同构成,分别是 (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]) 往回跳:
-
如果 (tr[p][c]) 不存在,那么 (tr[p][c]) 可以直接连到 (nxt) 。
-
如果 (tr[p][c]) 存在,且 (len[tr[p][c]]=len[p]+1) ,则 (link[nxt]=tr[p][c]) 。终止操作。
-
如果 (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 为其最长公共后缀。