KMP算法

97 阅读2分钟

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。

KMP算法

问题:如何由当前部分匹配结果确定模式向右滑动的新比较起点 k?

image.png

image.png

若干问题描述

1. P[0] ~ P[k-1] = P[j-k] ~ P[j-1] 说明了什么?

a. k 与 j 具有函数关系,由当前失配位置 j ,可以计算出滑动位置 k
b. 滑动位置 k 仅与模式串 P 有关

2. P[0] ~ P[k-1] = P[j-k] ~ P[j-1] 的物理意义是什么?

前者为长度为 k 的前缀,后者为长度为k的后缀

3. P[0] ~ P[j]中前缀和后缀相等的真子串唯一吗?

image.png

4. 模式应该向右滑多远才能保证算法的正确性?

image.png 设next[j]表示在匹配过程中与P[j]比较不相等时,下标 j 的回溯位置

image.png

image.png

  • j=0时, k=-1
  • j=1时, k=0
  • j=2时, P[0]≠P[1],因此,k = 0
  • j=3时, P[0]=P[2],P[0]P[1] ≠P[1]P[2],因此,k = 1
  • j=4时, P[0] ≠ P[3],P[0]P[1] = P[2]P[3],P[0]P[1]P[2]≠P[1]P[2]P[3],因此,k = 2

获取next函数值代码

void get_next(SString P, int next[]){
    int i = 0;   
    int j = -1;  
    next[0] = -1;
    while (i < P.length){
	if (j == -1 || P.ch[i] == P.ch[j]){
            i++;   j++;
            next[i] = j;
        }else{
            j = next[j];
    }
}

算法思想

  1. 在串 T 和串 P 中分别设比较的起始下标 i 和 j;
  2. 循环直到 T 或 P 的所有字符均比较完: 如果T[i]等于P[j],继续比较 T 和 P 的下一个字符;否则,将 j 向移动到next[j]位置,即 j=next[j]; 如果 j=-1,则将 i 和 j 分别加 1,准备下一趟比较;
  3. 如果 P 中所有字符均比较完毕,则返回匹配的起始下标;否则返回 0;