关于字符串匹配算法有不少,以前我有讲过一篇 KMP 匹配算法:图解字符串匹配 KMP 算法,不懂 kmp 的建议看下,写的还不错,这个算法虽然很牛逼,但在实际中用的并非特别多。至于选择哪种字符串匹配算法,在不一样的场景有不一样的选择。算法
在咱们平时文档里的字符查找里安全
采用的就是 Boyer-Moore 匹配算法了,简称BM算法。这个算法也是有必定的难度,不过今天,我选用一个例子,带你们读懂这个字符串匹配 BM 算法,看完这篇文章,保证你可以掌握这个算法的思想。数据结构
首先我先给出一个字符串和一个模式串工具
接下来咱们要在字符串中查找有没有和模式串匹配的字串,步骤以下:开发工具
一、
和其余的匹配算法不一样,BM 匹配算法,是从模式串的尾部开始匹配的,因此咱们把字符串和模式串的尾部对齐。3d
显然,从图中咱们能够发现,s 和 e 并不匹配。这时咱们把“s” 称之为坏字符,即表明不匹配的字符。并且咱们能够发现,s 和模式串中的任意一个字符都不匹配,因此这时,咱们能够直接把模式串移动到 s 的后面。视频
二、blog
从图中能够看出,此时 p 和 e 不匹配,因此 p 是一个坏字符,不过,咱们能够发现 “p” 包含在模式串中资源
因此,咱们不能像第一步那样,把模式串直接所有移到 p 的后面去,而是移动两位,让两个 p 对齐。开发
四、
那么问题来了,当咱们碰到碰到坏字符的时候,该移动几位呢?
下面我和你们讲一下这个问题,首先咱们要算出模式串中两个字符的下标。这两个字符分别是
(1)模式串中与坏字符对应的那个字符的下标,在咱们上面那个例子中,就是 e。
显然,这个 e 的下标是 6(从0开始算起)。咱们用变量 t1 来表明这个字符的下标吧。
(2)坏字符在模式串中的下标,在咱们上面那个例子中,坏字符在模式串中的下标为 4,咱们用变量 t2 来表明这个下标,如图
找出这两个字符的下标以后,咱们就能够计算移动的位数了
移动的位数 = t1 - t2。
例如上面的例子步骤 2 中 t1 = 6, t2 = 4,因此移动了 t1 - t2 = 2 位。
(1)这个时候可能有人会问了,那若是模式串中有多个 p 呢?
答是若是有多个,咱们只计算最右边的那个(固然是移动的位数越少越安全了)
(2)可能又有人会问,那若是模式串中并不存在坏字符呢?例如步骤1
答是若是不存在的话,咱们把 t2 赋值为 -1,即 t2 = -1。因此咱们步骤 1 中移动了 t1 - t2 = 6 - (-1) = 7 位。
好了,如今咱们已经解决了遇到坏字符以后,应该移动多少位的问题了。
咱们继续匹配
五、
匹配,因此继续匹配前面的字符
六、
匹配,继续匹配前面的字符
七、
匹配,继续匹配前面的字符
八、
匹配,继续匹配前面的字符
九、
遇到坏字符 i,按照咱们前面的规则,能够计算出 t1 = 2(就是a的下标)。t2 = -1(由于模式串不存在坏字符)。因此移动的位数是 t1 - t2 = 3。
可是,我想问一下,这是最好的移动方式吗?有没有更好的移动方法呢?接下来我就和你们介绍一种更好的方法,这种方法就是根据好后缀来移动位数。首先咱们先介绍下啥的好后缀。
在上面的例子中,咱们发现 "mple" 是可以成功匹配的
咱们把这些可以成功匹配的子串,称之为好后缀,因此呢,e,le,elp,mple 都是好后缀
由于 e, le, elp在以前的步骤中,也是可以成功匹配。不过 mple 是最长的好后缀。
接下来咱们要在模式串的前面寻找与好后缀匹配的子串,这句话的意思就是说,咱们要在模式串中寻找这样一个子串s:s 与好后缀匹配,而且s中的字符不能与好后缀有重叠。
我举个例子吧,例若有模式串 abcddab,而后好后缀分别是 b, ab, dab。那么与好后缀匹配的字串有 b,ab。(由于abcddab前面中的b能够与好后缀 b 匹配,前面的 ab 与好后缀 ab 匹配)。不过,没有与好后缀 dab 匹配的子串。
那么问题来了,若是咱们找到了多个这样的子串的话,咱们要选择哪个呢?例如上面咱们找到了两个,分别是 a,ab。
这个时候,咱们选择与比较长的那个好后缀匹配的子串,例如,上面的例子中,咱们会选择 ab,咱们把这个被选中的子串(ab)称之为好前缀吧(我是为了后面方便描述,才给它这个一个称呼)。
找出了好后缀和好前缀以后 ,咱们就能够知道要移动几位了,公式以下:
移动的位数 = 好后缀的下标 - 好前缀的下标。
固然,好后缀有多个,咱们是选择和好前缀匹配的那一个。那么好后缀的下标怎么算呢?,计算方法是按照好后缀的最后一个字符的下标为准,例如模式串 abcddab 中好后缀 ab 的下标为 6(下标从 0 开始算起)。好前缀下标的方法也是同样,以最后一个字符的下标位准,例如模式串 abcddab 中,好前缀 ab 的下标为 1。
这里可能有人会问,那若是不存在这样的好前缀呢?若是不存在的话,就用 -1 充当好前缀的下标。
知道了移动位数以后,咱们继续来匹配咱们上面的例子
十、
好后缀是 e, le, ple, mple,可是模式串中只有一个子串可以与好后缀 e 匹配,因此好前缀为 e。
显然,这个时候好前缀 e 的下标为 0,好后缀 e 的下标为 6,因此移动的位数为 6。若是按照咱们最开始坏字符的移动规则的话,只能移动 3 位,而用好后缀能够移动 6 位。
十一、
可能有人会问,两个规则咱们应该要选择哪个呢?
答案很简单,把两个规则的移动位数都算出来,选择移动位数多的就是了。
这里 p 是坏字符,而且不存在好后缀,因此采用坏字符的规则,移动 2 位。
十二、
这个时候,咱们能够发现,模式串的字符所有都匹配了,这也意味着匹配结束了。
这篇文章我是采用直接举例子的方式来说,我以为这样反而容易懂,而且在讲的过程当中,可能没有讲的那么全,这是由于我不想说的太全,由于把全部状况都罗列处理的话,相信你容易晕。因此我才用这种方式,让你先懂了这个 BM 的算法思想,以后的细节,你能够再去琢磨。
为了讲清楚这个算法,也算是绞尽脑汁,特别是为了可以以最简单的方式来说解好后缀的规则,停笔思索了很久,最后也百度搜索了几篇文章,看看别人都怎么讲,还翻开了我以前购买的数据结构与算法的专栏,,,最后结合本身的想法写了出来。但愿这篇文章,可以让你读懂给 BM 算法,这个算法的核心就是坏字符和好后缀了,相信你必定可以搞懂!后面会连续讲解几篇与树有关的文章。
若是你以为这篇内容对你挺有启发,为了让更多的人看到这篇文章:不妨
一、点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
二、关注我和专栏,让咱们成为长期关系
三、关注公众号「苦逼的码农」,主要写算法、计算机基础之类的文章,里面已有100多篇原创文章
大部分的数据结构与算法文章被各类公众号转载相信必定能让你有所收获
我也分享了不少视频、书籍的资源,以及开发工具,欢迎各位的关注个人公众号:苦逼的码农,第一时间阅读个人文章。