数据结构与算法之算法 字符串算法面试高频 KMP 前缀函数

数据结构与算法阿木 发布于 19 天前 5 次阅读


KMP 前缀函数:字符串匹配的利器

在计算机科学中,字符串匹配是一个基础且重要的算法问题。它广泛应用于文本编辑、信息检索、生物信息学等领域。KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,其核心思想是利用前缀函数来避免不必要的字符比较。本文将围绕KMP算法的前缀函数展开,探讨其原理、实现以及在实际应用中的优势。

前缀函数简介

在讨论KMP算法之前,我们先来了解一下前缀函数的概念。对于一个字符串`P`,其前缀函数`pi`是一个长度为`n`的数组,其中`pi[i]`表示`P`的前`i`个字符中,最长公共前后缀的长度。例如,对于字符串`P = "ABABAC"`,其前缀函数`pi`为`[0, 0, 1, 2, 3, 0]`。

前缀函数的推导

前缀函数的推导可以通过动态规划的方法实现。以下是一个简单的推导过程:

1. 初始化`pi[0] = 0`,因为空字符串的前缀长度为0。

2. 对于`i`从1到`n-1`,计算`pi[i]`的值。

- 如果`P[i] == P[pi[i-1]]`,则`pi[i] = pi[i-1] + 1`。

- 否则,如果`i > 1`,则`pi[i] = pi[i-1]`。

KMP算法原理

KMP算法的核心思想是利用前缀函数来避免在匹配过程中重复比较已经匹配过的字符。具体步骤如下:

1. 预处理模式串`P`,计算其前缀函数`pi`。

2. 将文本串`T`和模式串`P`分别初始化为指针`i`和`j`。

3. 当`i < n`且`j < m`时,进行以下操作:

- 如果`T[i] == P[j]`,则`i`和`j`同时递增。

- 如果`j == m`,则找到了一个匹配,返回匹配的起始位置`i - m`。

- 如果`T[i] != P[j]`,则根据前缀函数`pi`的值,将`j`更新为`pi[j-1]`,而不是`0`。

KMP算法实现

以下是一个使用Python实现的KMP算法示例:

python

def kmp_preprocess(P):


n = len(P)


pi = [0] n


j = 0


for i in range(1, n):


while j > 0 and P[i] != P[j]:


j = pi[j - 1]


if P[i] == P[j]:


j += 1


pi[i] = j


return pi

def kmp_search(T, P):


n, m = len(T), len(P)


pi = kmp_preprocess(P)


i, j = 0, 0


while i < n:


if T[i] == P[j]:


i += 1


j += 1


if j == m:


return i - j


elif i < n and T[i] != P[j]:


j = pi[j - 1]


else:


i += 1


return -1

示例


text = "ABABDABACDABABCABAB"


pattern = "ABABCABAB"


print(kmp_search(text, pattern)) 输出:10


KMP算法的优势

KMP算法相较于其他字符串匹配算法(如朴素算法、Boyer-Moore算法等)具有以下优势:

1. 时间复杂度:KMP算法的平均时间复杂度为O(n+m),其中n和m分别为文本串和模式串的长度。在最坏情况下,时间复杂度也为O(n+m)。

2. 空间复杂度:KMP算法的空间复杂度为O(m),其中m为模式串的长度。

3. 避免重复比较:KMP算法通过前缀函数避免了在匹配过程中重复比较已经匹配过的字符,从而提高了算法的效率。

总结

KMP算法的前缀函数是算法的核心,它能够帮助我们快速定位模式串在文本串中的位置。通过理解前缀函数的原理和实现,我们可以更好地掌握KMP算法,并在实际应用中发挥其优势。在字符串匹配领域,KMP算法是一个不可或缺的工具,值得深入研究和掌握。