[字符串]KMP算法实现字符串匹配

写在前面

本蒟蒻初学KMP,肯定会有很多错误,请大家指出

正文

目的

说道字符串匹配,大家首先想到的应该是朴素算法,一位一位匹配,匹配失败向右移动一位接着匹配,算法的时间复杂度是 O ( n ) O(n) O(n)

举一个简单的例子

A = “aababaabaab”
B = “aabaab”

示意图(制作不易,但为啥就是不动???)

暴力算法时间直接上天,这时候要是能自动跳过一些错误就好了

比如 :
示意图
直接跳到
示意图
就可以避免匹配一大堆了

怎么做到呢

流程

流程

刚刚的字符串的next数组是

示意图

你先别管它咋求,你先了解他咋用!!!

当他走到这里匹配失败了

示意图

我们此时不需要将B串右移一位,而是使 B串的第 1 位(B串的第next [ j ]位对着 i ) 对着 i 进行匹配(下标从0开始)

示意图

匹配失败,使 B串的第 0 位(B串的第next [ j ]位 ) 对着 i 进行匹配

示意图

此时匹配失败,但是此时 next [ j ] == -1,i 向右移

示意图

匹配成功,i 和 j 同时向右移

示意图

接下来一直右移就完成匹配了

next数组

刚刚的next数组是什么原理呢???

示意图
i = 0 , 前面没有字符 , next [ 0 ] = -1
i = 1 , 前面有字符 “a” , 字符串 “a” 最长的相同前后缀 的长度为 0
i = 2 , 前面有字符 “aa” , 字符串 “aa” 最长的相同前后缀为 “a” 长度为 1
i = 3 , 前面有字符 “aab” , 字符串 “aab” 最长的相同前后缀 的长度为 0
i = 4 , 前面有字符 “aaba” , 字符串 “aaba” 最长的相同前后缀为 “a” 的长度为 1
i = 5 , 前面有字符 “aabaa” , 字符串 “aabaa” 最长的相同前后缀为 “aa” 的长度为 2

能有点疑问:“为啥要求 最长的相同前后缀 ???”

很好理解,当你匹配到这个时候匹配失败了

示意图

意味着前面这一段蓝色的是相同的

示意图

当你求出它前面字符串相同前后缀的长度的时候

也就是黄色的这两段相同,并且黄色这两段的长度已经求出来了

示意图

所以红色的这两段相同,并且红色这两段的长度就等于黄色那段的长度

示意图

所以直接拿 B串下标1 对准 i 继续匹配即可

示意图

再举个栗子 :
A       ~~~~~       = “a a b b a b c a a b b a b a”
B       ~~~~~       = “a b b a b a”
next = " -1 0 0 0 1 1 "
当他匹配到这个位置

示意图
示意图
示意图

示意图

示意图

实现

咋求 next数组 ???

我们先来看看如何求最长相同前后缀

如果此时

  ~  i
ABAABCABAAB
      ~~~~~      j

可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移

    ~~~    i
ABAABCABAAB
        ~~~~~~~        j

可知此处 s[i] ≠ s[j] , i = next [i] = 0;意思是让 i 回退到上一次(由于当前求出的包含之前求出的)

  ~  i
ABAABCABAAB
        ~~~~~~~        j

可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移

    ~~~    i
ABAABCABAAB
           ~~~~~~~~~~           j

可知此处 s[i] = s[j] , next [j] = i + 1 = 2;
然后 i 往后移

      ~~~~~      i
ABAABCABAAB
             ~~~~~~~~~~~~             j

可知此处 s[i] ≠ s[j] , i = next [i] = 1;

    ~~~    i
ABAABCABAAB
             ~~~~~~~~~~~~             j

可知此处 s[i] ≠ s[j] , i = next [i] = 0;

  ~  i
ABAABCABAAB
             ~~~~~~~~~~~~             j

可知此处 s[i] ≠ s[j] , i = next [i] = -1;

当 i = -1 时 , next [j] = 0 , i = 0, 接着往下就行了

  ~  i
ABAABCABAAB
               ~~~~~~~~~~~~~~               j

可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移

    ~~~    i
ABAABCABAAB
                  ~~~~~~~~~~~~~~~~~                  j

可知此处 s[i] = s[j] , next [j] = i + 1 = 2;
然后 i 往后移

      ~~~~~      i
ABAABCABAAB
                    ~~~~~~~~~~~~~~~~~~~                    j

可知此处 s[i] = s[j] , next [j] = i + 1 = 3;
然后 i 往后移

        ~~~~~~~        i
ABAABCABAAB
                      ~~~~~~~~~~~~~~~~~~~~~                      j

可知此处 s[i] = s[j] , next [j] = i + 1 = 4;
然后 i 往后移

…………

相信大家已经有了初步的了解了,但是失配那一步为什么要 i = next [i] 呢???
这一步相当于一个回退

如图,假如相同颜色的字符串相同

突然这俩一灰一黑的不一样了,那咋办???

示意图

容易看出,next [i - 1] 是红色那一部分的长度 ,由于蓝色那一部分相同所以剩下的这两个红色是相同的
示意图
于是 i 回退到 next [i - 1] ,这两个继续比较

示意图注:这里求的是最长相同前后缀,next数组需要整体右移一位,next[0] 补 -1(因为 next 是指当前字符前方的所有字符,并不包括当前字符)

next 数组 代码实现 :

char s[10001]; 
int next[10001] = {0} ,len;
void nxt () {
 int j = 0 ,i = -1;
 while (j < len) {
  if (i == -1 || s[j] == s[i]) {
   next[++ j] = ++ i;
  }
  else {
   i = next[i];
  }
 }
}

匹配过程比较简单,大家可以自己写出来

后记

本蒟蒻如有错误,请大家指出

谢谢大家

——2020.8.27

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值