字符串匹配基础(上)

234 阅读4分钟

序言

​主要做一下记录,分为两篇文章记录,这一篇文章主要是记录一下单模式下的匹配算法,单模式主要是一个串在另一个字符串中的匹配,下一篇记录多模式下的匹配算法,就是一个串中匹配多个串的算法。

单模式下的两种算法BF和RK算法

BF算法

概括

BF算法中的BF是Brute Force 的缩写,中文叫做暴力匹配算法,也叫朴素匹配算法。从名字可以看出,这种算法的字符串匹配很**“暴力”**,当然也就会比较简单、好懂,但相应的性能也不高。

主串和模式串

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

img

BF的时间复杂度是O(N*M)

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

  1. 实际的软件开发中,大部分情况下,模式串和主串长度都不会太长,而且每次模式串与主串中的子串匹配的时候,当中遇到不能匹配的字符的时候,就可以停止了。不需要把m个字符都对比一下,所以,尽管理论上最坏的时间复杂度是O(N*M),但是统计意义上,大部分情况下,算法执行效率要比这个高很多。
  2. 朴素字符串匹配算法思想简单,代码实习也非常简单。简单意味着不容易出错,如果有bug也容易暴露和修复。在工程中,在满足性能要求前提下,简单是首选。这也是我们常说的KISS设计原则。

BF算法是字符串匹配算法中比较常用的匹配算法

RK算法

概述

RK算法的全程叫Rabin-Karp算法,是由它的两位发明者Rabin和Karp的名字来命名的,就是BF算法的升级版。

与BF算法的区别

BF,如果模式串长度为m,主串长度为n,那在主串中,就会有n-m+1个长度为m的子串,我们只需要暴力地比对这n-m+1个子串与模式串,就可以找出主串与模式串匹配的子串。

RK,通过哈希算法对主串中的n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比较大小,如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配了(这里先不考虑哈希冲突的问题,后面我们会讲到)。因为哈希值是一个数字,数字之间比较是否相等是非常快速的,所以模式串和子串比较的效率就提高了。

img

如何设计哈希算法

假设我们要匹配的字符串的字符集中只包含K个字符,我们可以用一个K进值来标识一个子串,这个K进值转化成十进制数,作为子串的哈希值。

举个栗子

假如我们要处理的字符串只包含a~z这26个小写字母,那我们就用二十六进制来表示一个字符串,a~z这26个字符映射到0~25这26个数字,a就表示0,b就表示1,一次类推,z表示25

如何计算进制

十进制:

“657” = 6*10*10+5*10+7*1

二十六进制:

“cba” = c*26*26+b*26+a*1

​ = 2*26*26+1*26+0*1

​ = 1353

RK算法怎么使用

RK算法包含两部分,计算子串哈希值和模式串哈希值与子串哈希值之间的比较,第一部分,我们需要设计一个哈希算法,只需要扫描一遍主串就能计算出所有子串的哈希值。所以时间复杂度是O(n)

模式串哈希值与每个子串哈希值之间的比较的时间复杂度是O(1),总共比较n-m+1个子串的哈希值,所以这部分的复杂度也是O(n)所以RK算法整体的时间复杂度就是O(n)

RK算法如何解决哈希冲突

当我们发现一个子串的哈希值跟模式串的哈希值相等时候,我们只需要再对比一下子串和模式串本身就好了。当然,如果子串的哈希值与模式串的哈希值不相等,那对应的子串和模式串肯定也是不匹配的,就不需要比对子串和模式串本身了。