### 后缀数组详解
#### 一、基本概念与定义
**后缀数组**是一种用于字符串处理的强大数据结构,尤其适用于解决与字符串相关的查询问题。本文将深入探讨后缀数组的概念、构造方法及其应用场景。
##### 字符集
- **定义**:字符集Σ是一个有序的集合,其中的元素可以进行大小比较。例如,ASCII码就是一个常见的字符集。
- **示例**:考虑Σ={a, b, c, …, z},在这个集合中,“a”小于“b”,依此类推。
##### 字符串与子串
- **字符串**:由一系列字符组成的序列,长度为n的字符串表示为S = [S[1], S[2], …, S[n]]。
- **子串**:字符串S的一个子串表示为S[i..j],即从S[i]到S[j]的所有字符组成的序列,其中1 ≤ i ≤ j ≤ n。
##### 后缀
- **定义**:对于字符串S,其从位置i开始的后缀记作Suffix(S, i) = S[i..n],即从位置i开始直到字符串结尾的所有字符组成的序列。
- **示例**:若S = "banana",则Suffix(S, 3) = "na"。
##### 字符串的比较
- **比较规则**:字符串的比较遵循字典序规则。若u、v为两个字符串,令i从1开始依次比较u[i]和v[i],直至找到第一个不同的字符或到达其中一个字符串的末尾。
##### 后缀数组
- **定义**:后缀数组SA是一个一维数组,保存着字符串S的所有后缀按字典序排序后的起始位置。即SA[1], SA[2], …, SA[n]表示S的所有后缀按从小到大的顺序排列后的起始位置。
##### 名次数组
- **定义**:名次数组Rank是一个辅助数组,对于每个位置i,Rank[i]表示后缀Suffix(i)在所有后缀中按字典序排序的排名。
#### 二、后缀数组的构造方法
**构造方法**是理解后缀数组的关键之一。一种常见的构造方法是**倍增算法**,它利用了后缀之间的关系来提高效率。
##### 倍增算法(Doubling Algorithm)
- **定义**:倍增算法是一种高效构造后缀数组的方法,通过逐步增加比较的前缀长度来实现排序。
- **步骤**:
1. **初始化**:设每个后缀的初始rank为S[i],即当前只比较单个字符。
2. **倍增**:每轮迭代时,将比较的前缀长度加倍,即从比较长度为k的前缀升级到比较长度为2k的前缀。
3. **排序**:在每一轮迭代中,根据上一轮得到的rank值对后缀进行排序。
4. **更新rank**:根据排序后的结果更新后缀的rank值。
- **时间复杂度**:O(n log n)。
##### k-前缀比较
- **定义**:对于字符串u,其k-前缀定义为u[k..min(len(u), k)],即u从第k个字符开始的子串,长度不超过k。
- **作用**:用于在倍增算法中比较后缀。
#### 三、后缀数组的应用
**后缀数组**的应用十分广泛,以下列举几个典型的应用场景:
1. **多模式串匹配**
- **问题描述**:给定一个文本T和多个模式P1, P2, …, Pm,找出所有模式在文本中的出现位置。
- **算法思想**:构建文本T的后缀数组,然后利用二分查找来寻找每个模式在后缀数组中的位置。
- **时间复杂度**:O(m + log n)。
2. **求最长回文子串**
- **问题描述**:给定一个字符串S,找到S中最长的回文子串。
- **算法思想**:利用后缀数组和LCP数组(最长公共前缀数组)来找出所有回文子串,并从中选取最长的一个。
- **时间复杂度**:O(n log n)。
#### 四、总结
后缀数组作为一种高效的数据结构,在处理字符串问题时有着重要的作用。通过本文的介绍,我们可以看到后缀数组不仅构造简单,而且在多种应用场景下都有良好的表现。对于信息学竞赛者来说,掌握后缀数组的原理和使用方法是非常有帮助的。