《数据结构》-BF算法和KMP算法

目录

 前言

第一章  BF算法

1.1  BF算法步骤

1.2  算法解析

1.3  总结

第二章 KMP 算法

2.1  KMP 算法解析

2.2  NEXT 函数修订值


 前言

        写本篇文章的动机源自于备战考研时,学习到《4.3.3 串的模式匹配算法》中的KMP算法时遇到瓶颈卡住了,在查阅其他教案和文档后终于理解,于是将自己的见解整理出来供大家参考,希望能够帮助大家学习进步。本文参考教材:《数据结构(C语言版)第二版》-严蔚敏


第一章  BF算法

1.1  BF算法步骤

        BF算法详细教案大家可以参考《数据结构(C语言版)第二版》中P89—P90中的内容,这里我就不做抄录了,本篇文章只做重点示意和难点解析。

【算法步骤】

①分别使用计数指针 i 和 j 指向主串 S 和字串 T。

②若主串 S 和字串 T均未比较至串尾部(即:主串 S 和字串 T 未匹配完成,i 和 j 的值分别小于S.length 和 T.length)则执行循环操作。

      将主串 S 和字串 T中的指针 i 和 j 所指向字符的值作比较:

      若相等:则 i 和 j 分别后移一位,继续比较后续字符,直到比较到队尾结束。

      若不相等:则子串 T 的指针 j 返回至子串 T 起始字符位置(j = 1),并从主串 S 上一次匹配的起始字符位置的下一位重新匹配(i = i - j + 2)。

③若 j > T.length 则说明,字串 T 的字符已全部对比并于主串 S 匹配完毕。

1.2  算法解析

【算法解析】

以《数据结构(C语言版) 第二版》教材P89-图4.4为例:

                                             i = 3

第一趟匹配   <主串S>  a b a b c a b c

                     <子串T>  a b c

                                           j = 3

                                       ↓  i = 3

第二趟匹配  <主串S>  a b a b c a b c

                    <子串T>     a

                                       ↑ j = 1

由图可知:在第一趟匹配中主串的指针 i = 3所在字符 与子串指针 j = 3 失配后,第二趟匹配字串直接从主串指针 i = 2 处开始匹配,那么引出问题主串的下一匹配字符公式(i = i - j + 2)如何推导?

以第一趟匹配为例:

① 当 i = 3 和 j = 3 匹配不成功时,意为 i = 3 和 j = 3 之前的字符全部匹配,所以先返回至匹配开始的位置,即主串 i = 1 处(标绿色的a),用 i = i - j + 1 可以计算出匹配起始位置,j 代表从主串匹配起始位置经过的距离(经过距离为 j = 3 ,子串已执行匹配的字符数量),因为主串和子串指针坐标从数字 1 开始,所以对 i - j  的结果 + 1 得出起始位置。

                                                i = 3 (失配)

第一趟匹配   <主串S>  a   b   a   b   c   a   b   c

                     <子串T>  a   b   c

                                          j = 3(失配)

                               j = 1    j = 2

② 对得出的起始位置再 + 1 ,意为从下一个字符开始匹配, i - j + 1 + 1 就是每次匹配失败后,都从是一次匹配开始的下一个字符开始重新匹配。

1.3  总结

        BF 算法核心就是沿着主串的初次匹配的字符开始,匹配失败后回到原点,再从下一个字符开始匹配。

第二章 KMP 算法

2.1  KMP 算法解析

       KMP 算法过程较为复杂,大家可以直接参考《数据结构(C语言版)第二版》中P90—P94中的内容,这里直接对算法进行解析。

【算法解析】

① 首先,我们要引入一个概念:“最长相等前后缀”。我们以教材P91(图 4.6)为例,模式串 t = {a b a a b c a c},截取部分{a b a a b},其前缀和后缀如下:

模式串 t = {a b a a b}

前缀 {a , ab , aba , abaa} 前缀排除最后一位。

后缀 {b , ab , aab , baab} 后缀排除第一位。

由此可见在图中所示的前缀和后缀集合中,只有{ab}为两者共有。

此时可知“最长相等前后缀”为{ab},长度值为“2”。

②理解“最长相等前后缀”的概念后,我们通过书中讲解可知,KMP 算法关键在于“主串 i 与子串 j 失配时,主串 i 不动,子串回溯从第 k 个元素开始和主串 i 匹配”,因此当出现失配时,子串从哪个位置的字符与主串 i 匹配就需要引入 NEXT 函数计算。

③这里我先给出 NEXT【j】函数的计算方法,之后再给出解析:

j 下标值 : 1  2  3  4  5  6  7  8

  子串 S : a  b  a  a  b  c  a  c

NEXT [ j ] : 0  1  1  2  2  3  1  2

NEXT [ j ] 的值为,第 j 前的(j - 1)个子串中,最长相等前后缀长度 + 1,当 j = 4 时,前(j - 1)个子串为{a b a},可见“最长相等前后缀”为{a},则NEXT 【j】的值为 1 + 1 = 2。表示当 j 和 i 不匹配时,子串 S 从下标 2 开始和主串 i 位置开始比较。

④这里需要弄清楚两个问题:

1.“最长相等前后缀长度”的作用。

2.为何NEXT 【j】的值为:“最长相等前后缀长度” + 1。

      i :  1 2 3 4 5 6 7 8

主串:a c a b a a b a a a b c

子串:      a b a a b c

      j :        1 2 3 4 5 6

 如上图所示,主串 i = 8 和子串 j = 6 (失配),我们可知主串的 3 至 7 和 子串的 1 至 5 是匹配的,同时通过NEXT 【j】计算子串的值,NEXT【6】 = 3,说明当 子串 j = 6 时失配后,子串要从下标 j = 3 处与 i 匹配,此时 j = 6之前的子串中“最长相等前后缀”为{ab}。

主串 : a b a a b a

子串 : a b a a b a

“最长相等前后缀” 可以理解为“前后重复长度”

 由于这一段{a b a a b}与主串匹配,且前后存在重复项,因此可以直接从“重复部分”的下一位开始比较。

总结:

1.“最长相等前后缀长度”的作用:找出主串和子串已匹配部分是否存在“重复部分”,再次匹配时直接从“重复部分”开始比较,节省时间。

2.为何NEXT 【j】的值为:“最长相等前后缀长度” + 1:j 的下标从1开始计算,所以计算结果要+ 1。

3.KMP 算法核心思想:当匹配过程中产生“失配”,指针 i 不变,指针 j 回溯到 NEXT【j】的值所示位置上重新匹配,当 i 和 NEXT【1】的值“失配”时,则主串和子串下标各 + 1,说明子串第一个字符和 i 所值字符不等 。

2.2  NEXT 函数修订值

      详见教材p94内容,此处要点为当子串和主串中存在大量相同的连续字符时,可以跳过相同字符,节省时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值