【算法】字符串匹配1 BF算法 RK算法

字符串匹配有多种方法,这里先讲最简单的两种算法: BF算法RK算法,复杂度也相对较高。
它们均为单模式串匹配的算法,也就是一个串跟一个串进行匹配。

BF算法
简介

Brute Force,暴力匹配算法,也叫朴素匹配算法。
比较简单、好懂,但相应的性能也不高。

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

算法思想

在主串中,检查起始位置分别是 0 、1、 2…n-m 且长度为 m 的 n-m+1 个子串,看有没有跟模式串匹配的。

BF

时间复杂度

极端情况下,要比较 n-m+1 次,每次需要比对 m 个字符。所以最坏情况时间复杂度O(n*m)

常用原因

虽然复杂度高,但实际开发中很常用,原因如下:

  • 大部分情况下,模式串和主串的长度都不会太长。每次模式串与主串中的子串匹配时,当中途遇到不能匹配的字符的时候,就可以就停止了,不需要把 m 个字符都比对;
  • 算法思想简单,代码实现简单。简单意味着不容易出错,如果有 bug 也容易暴露和修复。
RK算法

Rabin-Karp,由两位发明者名字而来。可以看做是BF的升级版。

算法思想

通过哈希算法对主串中的 n-m+1 个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。
如果某子串哈希值与模式串相等,那就说明对应子串和模式串匹配(先不考虑冲突)。
哈希值是一个数字,数字之间比较是否相等是非常快速。

RK

提高哈希计算效率

哈希算法计算子串的哈希值时,仍需要遍历子串中的每个字符,需要改进。

改进方法: 要匹配的字符串的字符集中只包含 K 个字符,可以用一个 K 进制数来表示一个子串,这个 K 进制数转化成十进制数,作为子串的哈希值。

举例解释上述方法。

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

再把二十六进制数转化成十进制数,进位从 10 改成 26 就可以。
在这里插入图片描述
这种计算方法,在主串中相邻两个子串的哈希值的计算公式有一定关系。
在这里插入图片描述

规律:相邻两个子串 s[i-1] 和 s[i] ( i 表示子串在主串中的起始位置,子串的长度都为 m ),对应的哈希值计算公式有交集。
公式表示如下:
在这里插入图片描述
事先计算好 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} 26m1 ,存储在长度为 m m m 的数组中,公式中的 “ 次方 ” 就对应数组的下标。
在这里插入图片描述

时间复杂度

第一部分:通过设计特殊的哈希算法,只需扫描一遍主串就能计算出所有子串的哈希值,所以时间复杂度是 O(n) 。
第二部分:需要比较 n-m+1 个子串的哈希值,比较的复杂度是O(1),总的复杂度也是 O(n)。

所以,RK 算法整体的时间复杂度就是O(n)

解决冲突问题

上面的哈希算法没有冲突,但是指数计算可能导致哈希值结果非常大,超出计算机表示范围。
解决方法就是允许冲突,从而减小计算范围。

允许冲突的哈希算法很多,举一个例子。

将每一个字母从小到大对应一个素数,把字符串中每个字母对应的数字相加,得到的和作为哈希值。

怎么解决冲突呢?其实很简单。
比较完哈希值后,
不相等的话,则无须比较,肯定不匹配;
相等的话,把两个字符串本身再比较一次即可。

这种方法前提是要控制冲突概率,达到可以接受的状态,否则算法会退化的厉害,变成 O(n*m) 。

本文是极客时间王争 数据结构与算法 课程的笔记,推荐此课,喜欢可以购买课程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值