字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配

字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配

字符串匹配算法:BF算法和RK算法,都是单模式串匹配算法,即一个串和另一个串进行匹配,BM算法和KMP算法是多模式串匹配算法,即一个串种同时查找多个串,分别是Trie树和AC自动机

RK算法是BF算法的改进,RK算法是如何借助哈希算法来实现高效字符串匹配的呢?

BF算法

暴力匹配算法,也叫朴素匹配算法

主串和模式串

在字符串A中查找字符串B,那A是主串,B就是模式串,把主串的长度记作n,模式串的长度记作m,因为是在主串中查找模式串,所以n>m

作为最简单、最暴力的字符串匹配算法,BF算法就是我们在主串中,检查起始位置分别是0,1,2……n-m且长度为m的n-m+1个子串,看有没有跟模式串匹配的

如果主串是“aaa……aaa”模式串是“aaaaab”,我们每次都对比m个字符,要对比n-m+1,最复杂的时间复杂度是 O ( n ∗ m ) O(n*m) O(nm)

BF算法时间复杂度虽然很高,但是是一个比较常用的字符串匹配算法:一:实际的软件开发中,模式串和主串的长度都不会太长,每次模式串和主串中的子串匹配的时候,当中途遇到不能匹配的字符的时候就停止了,不需要把m个字符都对比一下 二:朴素字符串匹配算法思想简单,不容易出错,有bug也容易暴露和修复

RK算法

RK算法是在BF算法的基础上改进的,对朴素字符串匹配算法改造,引入哈希算法,时间复杂度会降低

通过哈希算法对主串中的n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比较大小,如果某个子串的哈希值与模式串相等,说明对应的子串和模式串匹配了

此时 哈希算法的设计为:假设要匹配的字符串的字符集中只包含K个字符,我们可以用一个K进制数来表示一个子串,这个K进制数转化成十进制数,作为子串的哈希值。

要处理的字符串只包含a~z 26个小写字母,把这26进制来表示一个字符串,把a~z这26个字符映射到0~25这26个数字,a就表示0,b就是1,z就是25,在十进制的表示法中,一个数字的值是通过下面的方法计算的,对应到26进制,一个包含a到z这26字符的字符串,计算哈希值的时候,只需要把进位从10改成26即可
“ 657 ” = 6 ∗ 10 ∗ 10 + 5 ∗ 10 + 7 ∗ 1 “ c b a ” = " c " ∗ 26 ∗ 26 + " b " ∗ 26 + " a " ∗ 1 = 2 ∗ 26 ∗ 26 + 1 ∗ 26 + 0 ∗ 1 = 1353 “6 5 7” = 6 * 10 * 10 + 5 *10 + 7 * 1 “c b a ” = "c" * 26 * 26 + "b" * 26 + "a" * 1 = 2 * 26 * 26 + 1 * 26 + 0 * 1 = 1353 657=61010+510+71cba="c"2626+"b"26+"a"1=22626+126+01=1353
假设字符串中只包含26个小写字符,用26进制来表示一个字符串,对应的哈希值就是26进制转化成十进制的结果

这种哈希算法的主串中相邻两个子串的哈希值的计算公式有一定的关系:相邻两个子串s[i-1]和s[i](i表示子串在主串中的起始位置,子串的长度都为m),我们可以通过使用s[i-1]的哈希值可以很快算出s[i]的哈希值
h [ i − 1 ] 对 应 子 串 s [ i − 1 , i + m − 2 ] 的 哈 希 值 , h [ i ] 对 应 子 串 s [ i , i + m − 1 ] 的 哈 希 值 h [ i − 1 ] = 2 6 ( m − 1 ) ∗ ( s [ i − 1 ] − ′ a ′ ) + 2 6 ( m − 1 ) ∗ ( s [ i ] − ′ a ′ ) + … … + 2 6 0 ∗ ( s [ i + m − 2 ] − ′ a ′ ) h [ i ] = 2 6 ( m − 1 ) ∗ ( s [ i ] − ′ d ′ ) + … … + 26 ∗ ( s [ i + m − 2 ] − ′ a ′ ) + 2 6 0 ∗ ( s [ i + m − 1 ] − ′ a ′ ) 公 式 可 以 看 出 , B = A ∗ 26 , 所 以 h [ i ] 和 h [ i − 1 ] 的 关 系 是 : h [ i ] = ( h [ i − 1 ] − 2 6 ( m − 1 ) ∗ s [ i − 1 ] − ′ a ′ ) ∗ 26 + 2 6 0 ∗ ( s [ i + m − 1 ] − ′ a ′ ) h[i-1]对应子串s[i-1,i+m-2]的哈希值,h[i]对应子串s[i,i+m-1]的哈希值 h[i-1] = 26^(m-1)*(s[i-1]-'a')+26^(m-1)*(s[i]-'a')+……+26^0*(s[i+m-2]-'a') h[i] = 26^(m-1)*(s[i]-'d')+……+26*(s[i+m-2]-'a')+26^0*(s[i+m-1]-'a') 公式可以看出,B=A*26,所以h[i]和h[i-1]的关系是: h[i] = (h[i-1]-26^(m-1)*s[i-1]-'a') *26+26^0*(s[i+m-1]-'a') h[i1]s[i1,i+m2]h[i]s[i,i+m1]h[i1]=26(m1)(s[i1]a)+26(m1)(s[i]a)++260(s[i+m2]a)h[i]=26(m1)(s[i]d)++26(s[i+m2]a)+260(s[i+m1]a)B=A26,h[i]h[i1]h[i]=(h[i1]26(m1)s[i1]a)26+260(s[i+m1]a)
这里的26^(m-1)这计算,可以通过查表的方法来提高效率,事先计算好26^0、26^1、……26^(m-1),并且存储在一个m的数组中,计算26的x次方的时候,可以从数组下标为x的位置取值,直接使用

RK算法的时间复杂度是O(n)

如果在一个二维字符串矩阵(主串),如何在其中查找另一个二维字符串矩阵(模式串)?

KaTeX parse error: Undefined control sequence: \matrix at position 13: 主串=\left[ \̲m̲a̲t̲r̲i̲x̲{ d & a & b & …

KaTeX parse error: Undefined control sequence: \matrix at position 14: 模式串= \left[ \̲m̲a̲t̲r̲i̲x̲{ c & a\\ e &…

先查找第一行的字符串,如果长度为m,用bf或rk都可以,假如是n*n 的数组,bf复杂度是(n-m)*n,rk复杂度为n

如果有匹配,依次匹配第2到m行字符串,每次复杂度与第一次相同,所以复杂度不同

你可能感兴趣的:(数据结构与算法,跟宝宝一起学习)