📜  使用后缀数组对字符串的不同子字符串进行计数(1)

📅  最后修改于: 2023-12-03 15:36:38.777000             🧑  作者: Mango

使用后缀数组对字符串的不同子字符串进行计数

后缀数组是一种数据结构,可以快速地处理字符串相关问题。在计算机科学中,后缀数组是字符串处理中的一个重要概念,它允许我们高效地执行子字符串匹配、排序和计数等操作。

什么是后缀数组?

后缀数组是由一个字符串的所有后缀按字典序排列而成的数组。假设有一个字符串 $S$,$S_i$ 表示字符串 $S$ 的第 $i$ 个字符,它的后缀数组为 $SA$,则有:

$$ S[SA[1]..n] \lt S[SA[2]..n] \lt \cdots \lt S[SA[n]..n] $$

其中 $n$ 为字符串 $S$ 的长度。

如字符串 $S$ 为 banana 时,其后缀数组为: [5, 3, 1, 0, 4, 2],即:

n a n a n a
5         a
3       n a
1     n a n a
0   b a n a n a
4           n
2         a n
为什么使用后缀数组?

后缀数组主要用于字符串匹配问题,它可以高效地解决许多与字符串有关的问题,例如子串生成、子串匹配、最长公共子串等等。

以计算字符串的子串数为例,可以通过后缀数组来高效地实现,时间复杂度为 $O(n\log n)$,其中 $n$ 为字符串的长度。

如何使用后缀数组计数字符串的不同子串个数?

步骤如下:

  1. 建立后缀数组。
  2. 枚举所有后缀,计算其和前缀的 LCP(最长公共前缀)。
  3. 对所有 LCP 取和,得到字符串的不同子串数。

代码如下(Python 3):

def count_substrings(s):
    # 建立后缀数组
    n = len(s)
    SA = [i for i in range(n)]
    SA.sort(key=lambda x: s[x:])

    # 计算 LCP
    height = [0] * n
    h = 0
    for i in range(n):
        if h > 0:
            h -= 1
        if SA[i] == n - 1:
            continue
        j = SA[i] + 1
        while j + h < n and i + h < n and s[j + h] == s[i + h]:
            h += 1
        height[i] = h

    # 统计不同子串数
    cnt = n - SA[0]
    for i in range(1, n):
        cnt += n - SA[i] - height[i - 1]

    return cnt

参考文献:

  1. 后缀数组 - 维基百科
  2. 后缀数组(SA)及其应用 - CSDN博客