数据结构与算法-算法的应用-字符串匹配

如何利用哈希算法来实现字符串快速匹配?

BF 算法

BF 算法中的 BF 是 Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法。

具体实现就是 主串和从串。主串是大的那个字符串,从串是小的那个字符串。从主串的第一个字符开始匹配,如果没匹配上,从串后移一位,继续匹配。
数据结构与算法-算法的应用-字符串匹配_第1张图片
image.png

BF 算法的时间复杂度很高,是 O(n*m),但在实际的开发中,它却是一个比较常用的字符串匹配算法。为什么这么说呢?原因有两点。

第一,实际的软件开发中,大部分情况下,模式串和主串的长度都不会太长。而且每次模式串与主串中的子串匹配的时候,当中途遇到不能匹配的字符的时候,就可以就停止了,不需要把 m 个字符都比对一下。所以,尽管理论上的最坏情况时间复杂度是 O(n*m),但是,统计意义上,大部分情况下,算法执行效率要比这个高很多。

第二,朴素字符串匹配算法思想简单,代码实现也非常简单。简单意味着不容易出错,如果有 bug 也容易暴露和修复。在工程中,在满足性能要求的前提下,简单是首选。这也是我们常说的KISS(Keep it Simple and Stupid)设计原则

RK 算法

RK算法,就是BF算法的变种,把每个从串长度的主串数据,拿出来做哈希,得到的值再跟从串的哈希值进行比较,相同则继续比较数据是否相同。

这个算法的精髓在于,用哈希来取代了原本的数据字符比较,其实Java里也是这么实现字符串的eq的。

如何实现文本编辑器中的查找功能?

BF算法

BM 算法包含两部分,分别是坏字符规则(bad character rule)和好后缀规则(good suffix shift)。
BM算法的核心思想就是,用主串的数据去跟从串比,且从最后一位开始比。

坏字符规则

就是从主串与从串比较的时候,从主串的最后一位开始比较,如果从串里都没主串最后一位的数据,就可以直接把从串往后移动一个从串的长度。

按照这个道理,如果最后一位与从串中的哪个相等,则让其位置对其。
数据结构与算法-算法的应用-字符串匹配_第2张图片
image.png
好后缀规则

好后缀,就是主串最后几位与从串中的数据有重复,这几位就叫好后缀,那好后缀前面那个就是坏字符。此时我们把从串移到好后缀与头匹配的位置就行了。如下图:
数据结构与算法-算法的应用-字符串匹配_第3张图片
image.png

当模式串和主串中的某个字符不匹配的时候,如何选择用好后缀规则还是坏字符规则,来计算模式串往后滑动的位数?
我们可以分别计算好后缀和坏字符往后滑动的位数,然后取两个数中最大的,作为模式串往后滑动的位数。这种处理方法还可以避免我们前面提到的,根据坏字符规则,计算得到的往后滑动的位数,有可能是负数的情况。

如何借助BM算法轻松理解KMP算法?

KMP算法,用简单的话来说就是,主串不动,用从串首位开始跟主串比较,第一位没匹配上,就往后移一位继续,第一位匹配上,则拿从串第二位比主串当前位置的后一位。这个步骤让从串移动。
这里面会有一个问题,譬如,我从串前十位都匹配上了,第十一位没匹配上,此时我该移动多少位。

这个时候我们可以构建一个数组,用来存储模式串中每个前缀的最长可匹配前缀子串的结尾字符下标。我们把这个数组定义为next 数组,很多书中还给这个数组起了一个名字,叫失效函数(failure function)。如下图:
数据结构与算法-算法的应用-字符串匹配_第4张图片
image.png

失效函数:“匹配失败时不要直接返回到匹配前的位置,而是移动到已经部分匹配成功后的位置”

trie树

如何实现搜索引擎的搜索关键词提示功能?

对于多个单词,如何快速判断某个单词是否存在。
这样我们可以把所有单词用树来存储,每个节点包含一个字母。

例如:
数据结构与算法-算法的应用-字符串匹配_第5张图片
image.png

那么,代码层面如何实现这种结构呢?
首先我们定义tree,里面包含一个节点。

那么对于任意节点,都会有两个部分组成:
1.节点内部数据 。
2.下一个节点的位置。
对于第一点,我们可以定义一个字符变量来存储。
那么对于第二点来说,下一个节点是有a-z的26种可能,也就是说它最多会有26个子节点a-z。那么我们需要用一种数据结构来存储这26个指针。数组明显是最合适的。

那么结合上面两点来看,父节点的子节点数组位置指向了子节点,譬如arrray[2] 指向了当前节点,那是不是意味着 我就知道了这个节点 是 c 。 所以节点内部可以把字符变量去掉。这样所有有父节点的节点都能根据父节点找到自己的元素。
那么,没有父节点的那个元素呢?
没有父节点的那个元素,也就是树顶元素。我们是放在tree里面的。所以只要在tree里面定义一个内部变量,就能存储整个 字母开头的单词了。

如果此时我们仍然用节点本身当成第一个元素,那么是不是就可以把所有单词都存储进去。
或者我们弄一个哨兵,把tree的字符元素设为null。此时其实也是一个可以存储所有单词的树。

可以预见的是,trie树比较占用内存,如果数据比较稀疏,将会浪费很多内存。

如何利用 Trie 树,实现搜索关键词的提示功能?

我们假设关键词库由用户的热门搜索关键词组成。我们将这个词库构建成一个 Trie 树。当用户输入其中某个单词的时候,把这个词作为一个前缀子串在 Trie 树中匹配。为了讲解方便,我们假设词库里只有 hello、her、hi、how、so、see 这 6 个关键词。当用户输入了字母 h 的时候,我们就把以 h 为前缀的 hello、her、hi、how 展示在搜索提示框内。当用户继续键入字母 e 的时候,我们就把以 he 为前缀的 hello、her 展示在搜索提示框内。这就是搜索关键词提示的最基本的算法原理。

多模式串匹配算法

如何快速实现敏感词过滤?

如果我们把上面的trie树和KMP算法结合起来,就可以实现快速的敏感词过滤。-多模式串匹配算法 -AC自动机
AC 自动机算法,全称是 Aho-Corasick 算法。其实,Trie 树跟 AC 自动机之间的关系,就像单串匹配中朴素的串匹配算法,跟 KMP 算法之间的关系一样,只不过前者针对的是多模式串而已。所以,AC 自动机实际上就是在 Trie 树之上,加了类似 KMP 的 next 数组,只不过此处的 next 数组是构建在树上。

你可能感兴趣的:(数据结构与算法-算法的应用-字符串匹配)