Go 语言实现字符串匹配算法 -- BF(Brute Force) 和 RK(Rabin Karp)
今天介绍两种基础的字符串匹配算法,当然核心还是熟悉一下Go的语法,巩固一下基础知识
- BF(Brute Force)
- RK(Rabin Karp)
源字符串:src, 目标字符串:dest; 确认dest是否是src 的一部分。
BF算法很简单暴力,维护两个下标i,j,i控制src的遍历顺序, j控制dest遍历顺序。
记录一下i的起始位置,当j和i所在的字符匹配的时候,j和i都移动,知道j达到末尾则直接返回匹配。
否则i 回到起始位置的下一个位置,j 回到起始位置,两者重新进行匹配搜索。
由于比较简单,直接看一下Go实现的代码即可:
func Brute_force(str1 string, str2 string) bool{if len(str1) < len(str2){return false}var (begin int = 0 // 维护src的起始位置i intj int)for i = 0; i < len(str1); begin++ {for j = 0;j < len(str2); j++ {if i < len(str1) && str1[i] == str2[j] {i++continue} else {break}}if j == len(str2) {return true}i = begini++}return false
}
以上最坏的情况下是 主串是“aaaaa…aaaaaa”(省略号表示有很多重复的字符a),模式串是“aaaaab”。我们每次都比 对m个字符,要比对n-m+1次,所以,这种算法的最坏情况时间复杂度是O(n*m)。
当然这种模式匹配在实际的软件开发中还是比较常用的,主要有如下两种原因:
- 实际的软件开发中,大部分情况下,模式串和主串的长度都不会太长。而且每次模式串与主串中的子串匹配的时候,当中途遇到不能匹配的字符的时候, 就可以就停止了,不需要把m个字符都比对一下。所以,尽管理论上的最坏情况时间复杂度是O(n*m),但是,统计意义上,大部分情况下,算法执行效率要比这 个高很多。
- 朴素字符串匹配算法思想简单,代码实现也非常简单。简单意味着不容易出错,如果有bug也容易暴露和修复。
接下来看一下第二种算法,Rabin Karp。
这个算法的大体思路是:
过哈希算法对主串中的n-m+1个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相 等,那就说明对应的子串和模式串匹配了。因为哈希值是一个数字,数字之间比较是否相等是非常快速的, 所以模式串和子串比较的效率就提高了。
为了提升效率,我们需要尽可能避免遍历字符串中的每一个字符,否则就是仅仅提高了比较效率而已(数值的比较效率)。
hash函数的设计需要精巧一些,假设我们设置的字符集只包含k个字符,我们可以使用一个K进制的数来表示一个字符串,将这个 K进制转化为10进制的值即可作为hash 值。
比如要处理的字符串包括a-z 26个字母,我们可以使用26进制表示一个字符串,同时将26进制转化为一个10进制的值作为这个字符串的hash值。
对于字符串:“hjk”
h: (‘h’- ‘a’) *26^0
j: (‘j’ - ‘a’) * 26^1
j: (‘k’ - ‘a’) * 26^2
hash(“hjk”) = (‘h’- ‘a’) *26^0 + (‘j’ - ‘a’) * 26^1 + (‘k’ - ‘a’) * 26^2
为了减少遍历源字符串中的字符 次数,我们可以看看如下规律:
源字符串“hjkl”,我们先取 “hjk”, 后取"jkl"
hash(“hjk”) = (‘h’- ‘a’) *26^0 +('j' - 'a') * 26^1 + ('k' - 'a') * 26^2
hash(“jkl”) = ('j' - 'a') * 26^0 + ('k' - 'a')
* 26^1 + (‘l’ - ‘a’) * 26^2
依据此规律,我们可以总结出来两个公式:
hash(i-1) = 26^0 * (s[i-1] - ‘a’) + 26^1 * (s[i] - ‘a’) + … + 26^(m-1) * (s[i+m-2] -‘a’)
hash(i) = 26^0 * (s[i] - ‘a’) + … + 26^(m-2) * (s[i + m - 2] - ‘a’) + 26^(m-1) * (s[i+m-2] -‘a’)
i
表示源字符串的起始遍历位置字符下标,m
表示目标字符串的长度, s
表示源字符串 src。
两个公式进行运算:hash(i) - hash(i-1) / 26 = 26^(m-1) * (s[i+m-2] -‘a’) - (26^0 * (s[i-1] - ‘a’)) / 26
最终可以得到: hash(i) = (hash(i-1) - s[i-1] -‘a’ ) / 26 + 26^(m-1) * (s[i+m-2] -‘a’) 这样的计算公式。
这个时候,我们只需要扫描一遍主串即能够完成目标字符串的匹配, 主串的长度为n, 也就是我们需要O(n)的扫描复杂度。模式串的hash值 和每一个子串的hash值之间的比较是O(1) , 总共需要比较n-m+1个子串的哈希值,所以,这部分的时间复杂度也是O(n)。
所以,RK算法整体的时间复杂度就是O(n),相比于BF的O(m*n)效率还是高了不少。
Go语言的完整实现如下:
// Calculate a string's hash function
func Hash(str string, m [] int) int {if len(str) == 0 {return 0}var (t intres int = 0)for i := 0; i < len(str); i ++ {t = m[i] * int(str[i] - 'a')res = res + t}return res
}// match the substring with hash function
// we can calculate the string's hash value with below formula
//
// 's' is source string, m is the length of the substring
// h(i-1) = 26^0 * (s[i-1] - 'a') +
// 26^1 * (s[i] - 'a') + ... +
// 26^(m-1) * (s[i+m-2] -'a')
//
// h(i) = 26^0 * (s[i] - 'a') + ... +
// 26^(m-2) * (s[i + m - 2] - 'a') +
// 26^(m-1) * (s[i+m-2] -'a')
//
// so
// h(i) = (h(i-1) - s[i-1] -'a' ) / 26 + 26^(m-1) * (s[i+m-2] -'a')
// we can use the formula to reduce the cpu's calculation
func Rabin_Karp_Hash(str1 string, str2 string) bool {if len(str1) < len(str2) {return false}var m []intvar t int = 1m = append(m,1)for i := 1; i < len(str2) + 1; i ++ {t = t*26m = append(m,t) // m store with 26^0, 26^1, 26^2 ... 26^(len(str2))}str2_hash := Hash(str2, m)fmt.Println(str2_hash)str1_hash := Hash(string([]byte(str1)[:len(str2)]),m)if str2_hash == str1_hash {return true}for i := 1; i < len(str1) - len(str2) + 1; i ++ {new_hash := (str1_hash - int(str1[i-1]-'a')) / 26 +m[len(str2)-1] * int(str1[i+len(str2) -1] - 'a')if new_hash == str2_hash {return true} else {str1_hash = new_hash}}return false
}
Go 语言实现字符串匹配算法 -- BF(Brute Force) 和 RK(Rabin Karp)相关推荐
- 【GO语言实现字符串匹配算法-KMP算法】
[GO语言实现字符串匹配算法-KMP算法] KMP算法原理说明: KMP算法是一种改进的字符串匹配算法,是有D.E.Knuth,J.H.Morris和V.R.Pratt提出的,所以被称为KMP算法. ...
- 字符串匹配算法BF,BM,KMP
字符串匹配bf算法:(暴力穷举算法) 在一个字符串中寻找另一字符串,最容易想到的,也是最简单的办法是:取主串和模式串/搜索串中的每一位依次比较,如果匹配则同时后移一位继续比较,直至匹配到模式串的最后一 ...
- python实现字符串匹配算法BF,BF改,KMP
包含:BF,BF改进版本,KMP BF:暴力搜索 BF改:当判断匹配失败的字符串是不是与首字母相同 若不同,继续BF算法: 若相同,直接将首字母移到当前位置 KMP:通过前缀与后缀发现待匹配字符串本身 ...
- 字符串匹配KMP算法设计C语言,KMP字符串匹配算法笔记
网上有很多解释KMP算法的文章,A_B_C_ABC的这篇很详细,反复看了好几遍,总算理解了个大概,但是总觉得没那么爽快.其实,一种算法各人有各人的理解方法,找到适合自己理解的才容易记住.下面是我对这个 ...
- 字符串匹配算法---BF及KMP
字符串匹配的一般算法(BF) 以 ABSABABCEF 与 ABCE 为例,求串2与串1匹配的第一个位置的下标(这里即输出 5),一般的,我们可以从串1的起始位置开始与串2比较,若相同则两串都 ...
- 字符串匹配算法 -- BM(Boyer-Moore) 和 KMP(Knuth-Morris-Pratt)详细设计及实现
文章目录 1. 算法背景 2. BM(Boyer-Moore)算法 2.1 坏字符规则(bad character rule) 2.2 好后缀规则(good suffix shift) 2.3 复杂度 ...
- 列宽一字符等于多少厘米_字符串匹配算法总结——BF、KMP、BM
说明 以下算法介绍中,被匹配字符串称为主串,匹配模式字符串称为匹配串,索引从0开始. 前缀数组:字符串S = AB(B !== ⏀,即B为任一非空字符串) ,S的前缀指A.前缀数组指所有包含第一个字符 ...
- 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)
图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...
- iptables --algo 字符串匹配算法 bm kmp
http://blog.csdn.net/l953972252/article/details/51331001 字符串匹配一直是计算机领域热门的研究问题之一,多种算法层出不穷.字符串匹配算法有着很强 ...
最新文章
- 【MATLAB】三维图形的绘制mesh
- 完成登录功能,用session记住用户名
- 原创 | 常见损失函数和评价指标总结(附代码)
- border-raduis 在IE8中的兼容性问题
- 《Spark大数据分析:核心概念、技术及实践》一3.6 惰性操作
- 01.CSS动画--transform
- python基础----python的使用(四)
- Pearson相关系数 - Pearson's Correlation Coefficient
- 宜搭小技巧|找不到应用怎么办?群应用一键直达
- 基于docker使用jenkins集成sonar
- 当我们在讨论机器学习时我们在说些什么?
- 学了这么久的高并发编程,连Java中的并发原子类都不知道?这也太Low了吧
- 计算机与网络期刊多少钱,《计算机与网络》是不是核心期刊
- 电商十二、pinyougou02.sql的内容③
- hdu5294||2015多校联合第一场1007 最短路+最大流
- 橡皮擦的英语_小朋友们知道“橡皮擦”用英语该怎么说吗?
- Linux基于rhel的DNS部署(一)——高速缓存DNS
- 微信瑞文智力测试1分_答完瑞文智力测试的题之后,怎样看智商是多少?
- 普通打印机,双面打印技巧
- VMTK【1】Getting Started
热门文章
- Java中的文件上传2(Commons FileUpload:commons-fileupload.jar)
- maven插件报错之解决
- SharePoint使用BCS开发你第一个应用程序(三)
- 《帝企鹅日记》观后感
- [Ubuntu] 安装/卸载 声卡驱动
- 《OpenCV3编程入门》学习笔记7 图像变换(三 )重映射
- python测试程序的qps和响应时间代码_Python并发请求下限制QPS(每秒查询率)的实现代码...
- c语言计算M=11 22 33,四川计算机C语言考试笔试真题33次..doc
- java类引用接口的注释_java – 在接口类型上使用注释有什么好处?
- java tablemodel_高级组件——表格模型TableModel