KMP算法,全称Knuth-Morris-Pratt算法,是一种线性时间复杂度的字符串匹配算法。该算法主要用于在一个主文本字符串中查找一个词模式(子串)的出现位置。相比于暴力匹配算法,KMP算法具有更高的效率,因为它在发现不匹配时能够利用已经匹配的部分信息,从而避免不必要的重复比较。
一、算法原理
KMP算法的核心思想是利用已经匹配过的信息,当发生不匹配时,知道一些字符肯定不会出现在模式串的某个位置,从而利用这些信息跳过一些不必要的比较。具体来说,KMP算法通过维护一个“部分匹配表”(也称为“失败函数”或“跳转表”),来指导在发生不匹配时如何移动模式串。
部分匹配表是一个数组,其构建方法如下:对于模式串中的每个前缀子串,找出最长的、同时也是后缀的子串(不包括整个前缀子串本身),记录这个最长后缀子串的长度。部分匹配表的每个元素对应模式串的一个位置,存储的就是以该位置结尾的前缀子串的最长相同前后缀长度。
二、算法步骤
-
预处理阶段:构建部分匹配表。
- 初始化一个空的部分匹配表。
- 遍历模式串,对于每个位置
i
(从1开始计数),计算以i
结尾的前缀子串的最长相同前后缀长度。 - 将计算得到的长度值存入部分匹配表对应的位置。
-
匹配阶段:在主文本字符串中查找模式串。
- 初始化两个指针,一个指向主文本字符串的起始位置,另一个指向模式串的起始位置。
- 进入循环,逐个比较主文本字符串和模式串对应位置的字符。
- 如果字符匹配,则两个指针都向后移动一位。
- 如果字符不匹配,根据部分匹配表,将模式串的指针向后移动到合适的位置,主文本字符串的指针则保持不变。
- 重复上述步骤,直到模式串完全匹配或主文本字符串遍历完毕。
三、算法实现
下面是一个简单的KMP算法实现示例(以Python为例):
def compute_prefix_function(pattern):
m = len(pattern)
pi = [0] * m
j = 0
for i in range(1, m):
while j > 0 and pattern[j] != pattern[i]:
j = pi[j - 1]
if pattern[j] == pattern[i]:
j += 1
pi[i] = j
return pi
def kmp_search(text, pattern):
n = len(text)
m = len(pattern)
pi = compute_prefix_function(pattern)
q = 0 # index for text
r = 0 # index for pattern
while q < n and r < m:
if pattern[r] == text[q]:
q += 1
r += 1
elif r != 0:
r = pi[r - 1]
else:
q += 1
if r == m:
return q - r # pattern occurs with shift q - r
else:
return -1 # pattern does not occur in text
# 示例
text = "ABABDABACDABABCABAB"
pattern = "ABABCABAB"
result = kmp_search(text, pattern)
if result != -1:
print(f"Pattern found at index: {result}")
else:
print("Pattern not found in text.")
在这个示例中,compute_prefix_function
函数用于计算部分匹配表,而kmp_search
函数则执行实际的字符串匹配操作。当模式串在文本中找到时,函数返回模式串在主文本中的起始位置;否则返回-1表示未找到。
四、算法特点
KMP算法的时间复杂度为O(n+m),其中n是主文本字符串的长度,m是模式串的长度。这是因为KMP算法只需遍历主文本字符串和模式串各一次,并且在发生不匹配时能够利用部分匹配表进行高效的跳转,从而避免了不必要的比较操作。这使得KMP算法在处理大规模文本数据时具有显著的优势。
总的来说,KMP算法是一种高效且实用的字符串匹配算法,它利用部分匹配表来优化匹配过程,提高了字符串匹配的效率。在实际应用中,KMP算法被广泛用于文本搜索、生物信息学、网络爬虫等领域。