【KMP算法讲解】

总结一下KMP算法,求赞,求顶,求评论,求指教。。。

 1.

  首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

  2.

  因为B与A不匹配,搜索词再往后移。

  3.

  就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

  4.

  接着比较字符串和搜索词的下一个字符,还是相同。

  5.

  直到字符串有一个字符,与搜索词对应的字符不相同为止。

  6.

  这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

  7.

  一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

  8.

  怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

  9.

  已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

  因为 6 - 2 等于4,所以将搜索词向后移动4位。

  10.

  因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

  11.

  因为空格与A不匹配,继续后移一位。

  12.

  逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

  13.

  逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

  14.

  下面介绍《部分匹配表》是如何产生的。

  首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

  15.

  "部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

  16.

先看看next数据值的求解方法

位序          1    2    3    4    5    6    7    8
模式串       a    b    a    a    b    c    a    c
next值       0    1    1    2  2    3  1    2

next数组的求解方法是:
1.第一位的next值为0
2.第二位的next值为1
后面求解每一位的next值时,根据前一位进行比较
3.第三位的next值:第二位的模式串为b ,对应的next值为1;将第二位的模式串b与第一位的模式串a进行比较,不相等;则第三位的next值为1
4.第四位的next值:第三位的模式串为a ,对应的next值为1;将第三位的模式串a与第一位的模式串a进行比较,相同,则第四位的next值得为2
5.第五位的next值:第四位的模式串为a,对应的next值为2;将第四位的模式串a与第二位的模式串b进行比较,不相等;第二位的b对应的next值为1,则将第四位的模式串a与第一位的模式串a进行比较,相同,则第五位的next的值为2
6.第六位的next值:第五位的模式串为b,对应的next值为2;将第五位的模式串b与第二位的模式中b进行比较,相同,则第六位的next值为3
7.第七位的next值:第六位的模式串为c,对应的next值为3;将第六位的模式串c与第三位的模式串a进行比较,不相等;第三位的a对应的next值为1,则将第六位的模式串c与第一位的模式串a进行比较,不相同,则第七位的next值为1
8.第八位的next值:第七位的模式串为a,对应的next值为1;将第七位的模式串a与第一位的模式串a进行比较,相同,则第八位的next值为2
以上这种分析方法,位序是从1开始的,如果位序从0开始,刚第一位的next值为-1,后面的方法则相同

<think>嗯,用户想了解KMP算法,并且希望用Python来讲解。首先,我需要回忆一下KMP算法的核心思想。KMP算法主要用于字符串匹配,相比暴力匹配,它通过预处理模式串来避免重复比较,提高效率。关键点在于部分匹配表(也就是前缀函数或者next数组),这一步可能需要详细解释。 接下来,用户可能需要知道为什么需要KMP算法。暴力匹配的时间复杂度是O(mn),而KMP是O(m+n),这在处理大文本时优势明显。所以,我应该先对比一下暴力匹配和KMP的区别,突出其优势。 然后,部分匹配表的构建是关键。这部分可能比较抽象,需要举例说明。例如,对于模式串“ABABC”,如何生成next数组。可能需要分步骤解释每个位置的最长公共前后缀长度,以及如何利用这些信息在匹配失败时跳过不必要的比较。 用户可能对如何将理论转化为Python代码感兴趣。所以,我需要分步骤讲解如何构建next数组,然后如何利用这个数组进行匹配。代码部分需要简洁,同时要有注释,方便理解。另外,要注意处理边界情况,比如模式串为空或者文本比模式串短的情况。 另外,用户可能对KMP的实际应用场景不太清楚,可以举一些例子,比如文本编辑器中的查找功能或者DNA序列匹配等,说明其应用价值。 在解释过程中,要避免使用过于专业的术语,尽量用通俗的语言。同时,可能需要用例子来演示算法的执行过程,比如用具体的文本串和模式串,逐步展示匹配过程,以及next数组如何发挥作用。 还需要检查代码的正确性,确保没有语法错误,并且逻辑正确。比如,构建next数组时,双指针的移动是否正确,匹配过程中索引的处理是否恰当。可能需要用几个测试用例验证代码的正确性,比如模式串出现在文本开头、中间、末尾,或者多次出现的情况。 最后,总结KMP算法的优缺点,以及适用场景,帮助用户全面理解。可能还要指出,虽然KMP算法理论复杂度低,但在实际应用中,如果模式串不长或者文本串不大,暴力匹配可能更简单直接,而KMP在预处理上的开销是否值得需要根据具体情况判断。</think>### KMP算法核心思想 KMP算法(Knuth-Morris-Pratt)是一种**字符串匹配算法**,通过预处理模式串生成`next`数组(部分匹配表),在匹配失败时利用`next`值跳过不必要的字符比较,将时间复杂度优化至$O(m+n)$($m$为模式串长度,$n$为文本串长度)。 --- ### 核心概念:next数组 `next[i]`表示模式串`P[0..i]`中**最长公共前后缀**的长度。 **示例:** 模式串`P = "ABABC"`的`next`数组计算如下: $$ \begin{aligned} P[0] &= "A" &\rightarrow next[0] &= 0 \\ P[0..1] &= "AB" &\rightarrow next[1] &= 0 \\ P[0..2] &= "ABA" &\rightarrow next[2] &= 1 \quad (\text{前缀"A"和后缀"A"}) \\ P[0..3] &= "ABAB" &\rightarrow next[3] &= 2 \quad (\text{前缀"AB"和后缀"AB"}) \\ P[0..4] &= "ABABC" &\rightarrow next[4] &= 0 \end{aligned} $$ --- ### Python实现步骤 #### 1. 构建next数组 ```python def build_next(pattern): next = [0] * len(pattern) j = 0 # 前缀末尾指针 for i in range(1, len(pattern)): # i是后缀末尾指针 while j > 0 and pattern[i] != pattern[j]: j = next[j-1] # 回退到前一个匹配位置 if pattern[i] == pattern[j]: j += 1 next[i] = j return next ``` #### 2. 匹配过程 ```python def kmp_search(text, pattern): if not pattern: return 0 # 空模式直接匹配 next = build_next(pattern) j = 0 # 模式串指针 for i in range(len(text)): while j > 0 and text[i] != pattern[j]: j = next[j-1] # 根据next回退 if text[i] == pattern[j]: j += 1 if j == len(pattern): return i - j + 1 # 返回匹配起始位置 return -1 # 未找到 ``` --- ### 实例演示 **输入:** ```python text = "ABABABCABAABABABCA" pattern = "ABABC" ``` **执行过程:** 1. 生成`next = [0, 0, 1, 2, 0]` 2. 当匹配到`text[4]`时发现`'A' ≠ 'C'` 3. 根据`next[3]=2`回退到`pattern[2]`继续匹配 4. 最终在`text[2:7]`找到完整匹配 --- ### 关键优势 - **跳过无效比较**:通过`next`数组避免回溯文本指针 - **时间复杂度**:预处理$O(m)$ + 匹配$O(n)$,总体$O(m+n)$ - **空间复杂度**:$O(m)$(存储`next`数组) --- ### 应用场景 - 大文本中的关键字搜索 - 生物信息学中的DNA序列匹配 - 代码编辑器/IDE的查找替换功能 实际使用时可直接调用Python标准库`re`模块,但理解KMP原理对优化特殊场景的字符串处理至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值