【算法】KMP算法

版权声明:转载先点个赞嘛~~ https://blog.csdn.net/qq_39011762/article/details/88406586

一. 暴力匹配

字符串匹配的最直接的方法就是暴力匹配,而KMP算法也是基于暴力算法进行改进。暴力匹配的思想如下:

  1. 对于文本串T和模式串P,从模式串P的第 0 号位置、文本串第 i 0 i_0 号位置开始逐一比对;
  2. 比对到中间某个时刻,若 T [ i ] = = P [ j ] T[i ] == P[j] ,则比对继续进行, i + + , j + + i++, j++
  3. 如果比对失败,则从模式串第0位和文本串的第 i 0 + 1 i_0 + 1 位继续进行
    在这里插入图片描述

但是 T [ i 0 , i ) T[i_0 , i) P [ 0 , j ) P[0, j) 比对成功意味着 T [ i 0 , i ) T[i_0 , i) P [ 0 , j ) P[0, j) 完全相同的,掌握了 P [ 0 , j ) P[0, j) 那么也就意味着掌握了 T [ i 0 , i ) T[i_0 , i) ,下一轮的比对方案完全可以提前预知。

例1
在这里插入图片描述
在图中的比对过程中,主串中的 x 和模式串中的 y 失配,根据模式串中 y 以前的内容可以获知主串对应部分的内容。如果在下一步的比对过程中直接将主串中的 x 和模式串中的 e 进行比对,可以省去 6 次比对

二.KMP的基本思想

在对暴力破解的算法的分析中发现,对比在某个位置失败意味着在这之前的比对完全成功,主串中失配字符前的一段内容已经完全获知。利用这一点,对暴力算法可以进行两个方面的优化:

  1. 避免主串的回溯。暴力匹配当比对失败后,文本串的第 i 0 + 1 i_0 + 1 位、 i 0 + 2 i_0 + 2 位、……、 i 1 i-1 位的对比结果完全可以推导出来,没有必要再进行比对尝试;
  2. 模式串快速移动。基于和上面相同的原因,模式串的新的比对位置不需要从 0 开始。如例一中的模式串,字符y和字符e的前面都包含了"abc"这一部分,因此y前面的部分能和主串匹配成功,那么e前面的也一定能匹配成功。

因此对于模式串中的每个位置 j ,都能提前找到一个替代位置。

例2
在这里插入图片描述

模式串"abababca",对字符c而言,2号位的 a 和4号位的 a 都是能够在字符 c 发生失配时的一个可选择的位置

在诸多可选的继任位置中,位置下标越大,意味着已经成功匹配的长度越长,剩下需要比对的位置也就越少,因此 j 的继任位置 n e x t [ j ] next[j] 定义为:

n e x t [ j ] = max ( k p 0 p 1 . . . p k 1 = p j k p j k + 1 . . . p j 1 ) next[j] = \max(k | p_0 p_ 1...p_{k - 1} = p_{j - k}p_{j - k + 1}...p_{j - 1})

扫描二维码关注公众号,回复: 5893342 查看本文章

通常定义 n e x t [ 0 ] = 1 next[0] = -1 或者 n e x t [ 1 ] = 0 next[1] = 0 (当字符串的下标从1开始时),这种规定是假想在模式串的起始位置的前一个有一个通配哨兵。

三.next[]的求法

1. 暴力求解

根据 n e x t [ j ] next[j] 的定义,从逐一枚举字符P[j]的真前缀和真后缀,找出相等的真前缀和真后缀的长度,取长度的最大值即为 n e x t [ j ] next[j]

2. 递推求解

假设已经求得 n e x t [ 0 , . . . , j ] next[0, ... , j] ,递推求解 n e x t [ j + 1 ] next[j + 1]

n e x t [ j ] next[j] 已知意味着 P [ 0 , 1 , . . . , n e x t [ j ] 1 ] P[0, 1, ..., next[j] - 1] P [ j n e x t [ j ] , . . . , j 1 ] P[j - next[j], ... , j - 1] 是相等的,并且这个相等的部分是最大的,求取 n e x t [ j + 1 ] next[j + 1] 时,只需要考察 P [ n e x t [ j ] ] = = P [ j ] P[next[j]] == P[j] 是否成立。如果成立, n e x t [ j + 1 ] = n e x t [ j ] + 1 next[j + 1] = next[j] +1 ,如果不成立,再考察 P [ n e x t [ n e x t [ j ] ] ] = = P [ j ] P[next[next[j]]] == P[j] 是否成立,依次类推,最终会收敛于 n e x t [ 0 ] + 1 = 0 next[0] + 1 = 0
在这里插入图片描述
插图来自视频

void buildNext(string str, int nt[]){
    int len = str.size();
    nt[0] = -1;
    int t = nt[0], j = 0;
    while(j < len - 1){
        if(t < 0 || str[j] == str[t]){
            nt[++j] = ++t;
        }else{
            t = nt[t];
        }
    }
}

四.KMP算法

在求解了next数组之后,kmp算法变得非常简单了。

int kmp(string str1, string str2){
    int nt[str2.size()];
    buildNext(str2, nt);//构建next表
    int i = 0, j = 0;
    while(i < str1.size() && j < str2.size()){//逐步比对
        if(j < 0 || str1[i] == str2[j]){//比对成功时,前进一位,j < 0表示和通配符比对成功
            i++; j++;
        }else{//对比失败,找到新的位置比对
            j = nt[j];
        }
    }
    return i - j;
}

猜你喜欢

转载自blog.csdn.net/qq_39011762/article/details/88406586
今日推荐