字符串匹配算法就是将从一个字符串(主串,长度为n)中匹配出指定的字符串(模式串,长度为m)。字符串查找函数比如Python中的find()函数、word里的查找替换等,底层依赖的就是字符串匹配算法。
BF算法全称Brute Force算法,又叫作暴力匹配算法、朴素匹配算法。
BF算法就是在主串中,检查起始位置分别是0、1、2 … n-m且长度为m的n-m+1个子串,看有没有跟模式串匹配的。
最坏时间复杂度: O ( n ∗ m ) O(n*m) O(n∗m)
适用范围:模式串和主串长度都不太长的实际软件开发中。
RK算法全称Rabin-Karp算法,是BF算法的升级版,比较的是子串和模式串的哈希值,而不需要逐个字符比对。具体的,通过哈希算法,对主串中的n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比较,如果某个子串的哈希值与模式串相等,就说明匹配上了。
不过通过哈希算法计算子串的哈希值时,需要遍历子串中的每个字符,尽管模式串与子串比较的效率提高了,但是算法的整体效率并没有提高,有什么办法可以提高哈希算法计算子串哈希值的效率呢?
假设要主串的字符集只包含K个字符,就可以用K进制数来表示一个子串,把这个K进制数转化成十进制数,作为子串的哈希值,例如:
这样有个好处,相邻的两个子串s[i-1]和s[i]有重叠的部分,s[i]的哈希值可以通过s[i-1]的哈希值很快计算出来:
另外, 2 6 0 26^0 260、 2 6 1 26^1 261、 2 6 2 26^2 262、… 、 2 6 ( m − 1 ) 26^(m-1) 26(m−1)可以预先计算后存到一个长度为m的数组里,下标分别为0、1、2、…、m-1,用的时候可以查表。
如果主串和模式串比较长,上面的哈希算法会得到很大的哈希值,超出整型的表示范围,这时可以牺牲一下允许哈希冲突。在存在哈希冲突的情况下,如果子串和模式串的哈希值相等,还需要再比对一下子串和模式串自身。
时间复杂度: O ( n ) O(n) O(n) = 哈希值计算 O ( n ) O(n) O(n) + 比较 O ( n ) O(n) O(n)
最坏时间复杂度:如果存在大量哈希冲突,就会退化成 O ( m ∗ n ) O(m*n) O(m∗n)
如何在一个二维字符串矩阵中,查找另一个二维字符串矩阵呢?
计算主串中2*2子串的哈希值,与模式串的哈希值比对。
BM算法全称是Boyer-Moore算法,是一种非常高效的字符串匹配算法,性能是KMP算法的3到4倍,是最高效、最常用的字符串匹配算法。BM算法的思路是,当模式串与主串某个字符不匹配时,能够跳过一些肯定不会匹配的情况,将模式串往后多滑动几位。
BM算法包含两部分:坏字符规则(bad character rule)、好后缀规则(good suffix shift),取两个数中最大的,作为模式串往后滑动的位数。这样可以避免仅用坏字符规则结果为负的情况。但这样不会漏吗?不会,因为两个都已经是各自满足条件的最小距离了,如果取较小那一个,另一个一定不满足条件。
BM算法最好情况时间复杂度是 O ( n / m ) O(n/m) O(n/m),最坏情况时间复杂度是 O ( n ) O(n) O(n)。
KMP算法全称Knuth Morris Pratt算法,是字符串匹配算法中最知名的一种,但不太可能自己亲手实现一个KMP算法,主要思路跟BM算法非常接近,也是找到可以将模式串往后多滑动几位的规律。
KMP算法包含两部分:坏字符规则(bad character rule)、好前缀规则(good prefix shift)。
KMP算法的时间复杂度是 O ( m + n ) O(m+n) O(m+n)。
Trie树又叫字典树,是一个树形结构,适合在多个字符串中查找与模式串前缀匹配的字符串,常见应用是搜索关键词提示、自动输入补全等功能。
思路:用各主串的字符串的公共前缀构建字符树,以减少重复查找。
结构:Trie树是一个多叉树。根节点不包含任何信息,每个节点表示一个字符,从根节点到红色节点(不一定是叶子节点)的一条路径表示一个字符串。
构造过程:字符串的插入。时间复杂度是 O ( n ) O(n) O(n),n表示多有字符串的长度和。
查找过程:从根节点开始,沿着某条路径来匹配,如果路径的最后一个节点是红色的就可以匹配,如果最后一个节点不是红色的则不能匹配。时间复杂度是 O ( k ) O(k) O(k),k是要查询的字符串的长度。
存储:多叉树的指针可以用vector
如果是在多个字符串中查找与模式串精确匹配的字符串,而不是前缀匹配的字符串,则更适合看作一个数据查找问题,用散列表、红黑树、跳表来实现,原因是,对于Trie树:
AC自动机算法,全称是Aho-Corasick算法,用于在单个主串里查找多个模式串的场景,可应用在敏感词过滤上。AC自动机实际上就是在Trie树之上,加了类似KMP的next数组,让匹配失败时,尽可能将模式串往后多滑动几位。
AC自动机的构建: