KMP讲解资料


BF:暴力匹配法O(n*m)

如果用暴力匹配的思路
并假设现在

文本串S匹配到 i 位置
模式串P匹配到 j 位置
则有:

如果当前字符匹配成功(即S[i] == P[j])
        则i++,j++,继续匹配下一个字符;
如果失配(即S[i]! = P[j])
        令i = i - (j - 1),j = 0。
        相当于每次匹配失败时,i 回溯,j 被置为0。

S = "BBC ABCDAB ABCDABCDABDE"
p = "ABCDABD"

1.
BBC ABCDAB ABCDABCDABDE
ABCDABD

2.
BBC ABCDAB ABCDABCDABDE
 ABCDABD

3.
BBC ABCDAB ABCDABCDABDE
  ABCDABD

BBC ABCDAB ABCDABCDABDE
   ABCDABD
4.
BBC ABCDAB ABCDABCDABDE
   ABCDABD
5.
BBC ABCDAB ABCDABCDABDE
    ABCDABD
6.
BBC ABCDAB ABCDABCDABDE
     ABCDABD
7.
BBC ABCDAB ABCDABCDABDE
      ABCDABD






KMP:可以实现复杂度为O(m+n)

下面先直接给出KMP的算法流程
假设现在
文本串S匹配到 i 位置,
模式串P匹配到 j 位置

如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),
        都令i++,j++,继续匹配下一个字符;
如果j != -1,且当前字符匹配失败(即S[i] != P[j]),
        则令 i 不变,j = next[j]。
        此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。

步骤:
S = "BBC ABCDAB ABCDABCDABDE"
p = "ABCDABD"

1.
BBC ABCDAB ABCDABCDABDE
ABCDABD

2.
BBC ABCDAB ABCDABCDABDE
 ABCDABD

3.
BBC ABCDAB ABCDABCDABDE
  ABCDABD

BBC ABCDAB ABCDABCDABDE
   ABCDABD
4.
BBC ABCDAB ABCDABCDABDE
    ABCDABD
5.
BBC ABCDAB ABCDABCDABDE
    ABCDABD
6.
BBC ABCDAB ABCDABCDABDE
        ABCDABD
7.
BBC ABCDAB ABCDABCDABDE
          ABCDABD

8.
BBC ABCDAB ABCDABCDABDE
           ABCDABD

9.
BBC ABCDAB ABCDABCDABDE
               ABCDABD
....



#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1000;

int  next[MAXN];

int plen,slen;
void GetNext(char *p,int plen)
{
        int i,j;
        next[0]=j=-1;
        i=0;
        while(i<plen)
        {
                if( -1 == j ||p[i] == p[j])
                {
                        i++;
                        j++;
                        next[i]=j;
                }
                else
                        j=next[j];
        }
}

int KMP(char *s,char *p,int slen,int plen)
{
        int i=0,j=0;
        while(i<slen&&j<slen)
        {
                if(j==-1||s[i]==p[j])
                {
                        i++;
                        j++;
                }
                else
                        j=next[j];
                if(j==plen)     return i-j;
        }
        return -1;
}

int main()
{
        char s[] = "BBC ABCDAB ABCDABCDABDE";
        char p[] = "ABCDABD";
        plen = strlen(p);
        slen = strlen(s);
        GetNext(p,plen);

        printf("%d\n",KMP(s,p,slen,plen));
        return 0;
}

<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、付费专栏及课程。

余额充值