数据结构与算法之美
王争
前Google工程师
立即订阅
71638 人已学习
课程目录
已完结 75 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 从今天起,跨过“数据结构与算法”这道坎
免费
入门篇 (4讲)
01 | 为什么要学习数据结构和算法?
02 | 如何抓住重点,系统高效地学习数据结构与算法?
03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度
基础篇 (38讲)
05 | 数组:为什么很多编程语言中数组都从0开始编号?
06 | 链表(上):如何实现LRU缓存淘汰算法?
07 | 链表(下):如何轻松写出正确的链表代码?
08 | 栈:如何实现浏览器的前进和后退功能?
09 | 队列:队列在线程池等有限资源池中的应用
10 | 递归:如何用三行代码找到“最终推荐人”?
11 | 排序(上):为什么插入排序比冒泡排序更受欢迎?
12 | 排序(下):如何用快排思想在O(n)内查找第K大元素?
13 | 线性排序:如何根据年龄给100万用户数据排序?
14 | 排序优化:如何实现一个通用的、高性能的排序函数?
15 | 二分查找(上):如何用最省内存的方式实现快速查找功能?
16 | 二分查找(下):如何快速定位IP对应的省份地址?
17 | 跳表:为什么Redis一定要用跳表来实现有序集合?
18 | 散列表(上):Word文档中的单词拼写检查功能是如何实现的?
19 | 散列表(中):如何打造一个工业级水平的散列表?
20 | 散列表(下):为什么散列表和链表经常会一起使用?
21 | 哈希算法(上):如何防止数据库中的用户信息被脱库?
22 | 哈希算法(下):哈希算法在分布式系统中有哪些应用?
23 | 二叉树基础(上):什么样的二叉树适合用数组来存储?
24 | 二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?
25 | 红黑树(上):为什么工程中都用红黑树这种二叉树?
26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树
27 | 递归树:如何借助树来求解递归算法的时间复杂度?
28 | 堆和堆排序:为什么说堆排序没有快速排序快?
29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?
30 | 图的表示:如何存储微博、微信等社交网络中的好友关系?
31 | 深度和广度优先搜索:如何找出社交网络中的三度好友关系?
32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?
33 | 字符串匹配基础(中):如何实现文本编辑器中的查找功能?
34 | 字符串匹配基础(下):如何借助BM算法轻松理解KMP算法?
35 | Trie树:如何实现搜索引擎的搜索关键词提示功能?
36 | AC自动机:如何用多模式串匹配实现敏感词过滤功能?
37 | 贪心算法:如何用贪心算法实现Huffman压缩编码?
38 | 分治算法:谈一谈大规模计算框架MapReduce中的分治思想
39 | 回溯算法:从电影《蝴蝶效应》中学习回溯算法的核心思想
40 | 初识动态规划:如何巧妙解决“双十一”购物时的凑单问题?
41 | 动态规划理论:一篇文章带你彻底搞懂最优子结构、无后效性和重复子问题
42 | 动态规划实战:如何实现搜索引擎中的拼写纠错功能?
高级篇 (9讲)
43 | 拓扑排序:如何确定代码源文件的编译依赖关系?
44 | 最短路径:地图软件是如何计算出最优出行路径的?
45 | 位图:如何实现网页爬虫中的URL去重功能?
46 | 概率统计:如何利用朴素贝叶斯算法过滤垃圾短信?
47 | 向量空间:如何实现一个简单的音乐推荐系统?
48 | B+树:MySQL数据库索引是如何实现的?
49 | 搜索:如何用A*搜索算法实现游戏中的寻路功能?
50 | 索引:如何在海量数据中快速查找某个数据?
51 | 并行算法:如何利用并行处理提高算法的执行效率?
实战篇 (5讲)
52 | 算法实战(一):剖析Redis常用数据类型对应的数据结构
53 | 算法实战(二):剖析搜索引擎背后的经典数据结构和算法
54 | 算法实战(三):剖析高性能队列Disruptor背后的数据结构和算法
55 | 算法实战(四):剖析微服务接口鉴权限流背后的数据结构和算法
56 | 算法实战(五):如何用学过的数据结构和算法实现一个短网址系统?
加餐:不定期福利 (6讲)
不定期福利第一期 | 数据结构与算法学习书单
不定期福利第二期 | 王争:羁绊前行的,不是肆虐的狂风,而是内心的迷茫
不定期福利第三期 | 测一测你的算法阶段学习成果
不定期福利第四期 | 刘超:我是怎么学习《数据结构与算法之美》的?
总结课 | 在实际开发中,如何权衡选择使用哪种数据结构和算法?
《数据结构与算法之美》学习指导手册
加餐:春节7天练 (7讲)
春节7天练 | Day 1:数组和链表
春节7天练 | Day 2:栈、队列和递归
春节7天练 | Day 3:排序和二分查找
春节7天练 | Day 4:散列表和字符串
春节7天练 | Day 5:二叉树和堆
春节7天练 | Day 6:图
春节7天练 | Day 7:贪心、分治、回溯和动态规划
加餐:用户学习故事 (2讲)
用户故事 | Jerry银银:这一年我的脑海里只有算法
用户故事 | zixuan:站在思维的高处,才有足够的视野和能力欣赏“美”
结束语 (3讲)
结束语 | 送君千里,终须一别
第2季回归 | 这一次,我们一起拿下设计模式!
打卡召集令 | 60 天攻克数据结构与算法
免费
数据结构与算法之美
登录|注册

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

王争 2018-12-05
从今天开始,我们来学习字符串匹配算法。字符串匹配这样一个功能,我想对于任何一个开发工程师来说,应该都不会陌生。我们用的最多的就是编程语言提供的字符串查找函数,比如 Java 中的 indexOf(),Python 中的 find() 函数等,它们底层就是依赖接下来要讲的字符串匹配算法。
字符串匹配算法很多,我会分四节来讲解。今天我会讲两种比较简单的、好理解的,它们分别是:BF 算法和 RK 算法。下一节,我会讲两种比较难理解、但更加高效的,它们是:BM 算法和 KMP 算法。
这两节讲的都是单模式串匹配的算法,也就是一个串跟一个串进行匹配。第三节、第四节,我会讲两种多模式串匹配算法,也就是在一个串中同时查找多个串,它们分别是 Trie 树和 AC 自动机。
今天讲的两个算法中,RK 算法是 BF 算法的改进,它巧妙借助了我们前面讲过的哈希算法,让匹配的效率有了很大的提升。那RK 算法是如何借助哈希算法来实现高效字符串匹配的呢?你可以带着这个问题,来学习今天的内容。

BF 算法

BF 算法中的 BF 是 Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法。从名字可以看出,这种算法的字符串匹配方式很“暴力”,当然也就会比较简单、好懂,但相应的性能也不高。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《数据结构与算法之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(105)

  • Jerry银银
    觉得今天的hash算法真是巧妙
    2018-12-05
    4
    89
  • ZX
    RK算法,在对主串构建的时候,就对比是不是一样的,一样就不继续计算后面的hash,这样会不会更快一点

    作者回复: 你说的很对 代码实现就是这样处理的

    2018-12-06
    51
  • P@tricK
    思考题:
    假设二维主串和模式串的维度分别是 m*n 和 i*j,横向在[0, m-i],纵向在[0, n-j]取起始点,然后取同样的子串窗口对比,共有(m-i+1)*(n-j+1)个子串。

    ps:
    文中计算子串哈希值h[i]的公式中,第二个h[i-1]和后面的h[i+m-1],应该是主串中的第i-1个和第i+m-1个字符的哈希值…
    2018-12-05
    21
  • Smallfly
    思考题:

    以模式串矩阵的大小,去匹配主串矩阵,每个小矩阵可以构建成字符串,就能用 RK 算法做字符串匹配了。

    如果主串的大小是 M * N,模式串大小为 m * n,则时间复杂度为 (M - m + 1) * (N - n + 1)。
    2018-12-05
    1
    17
  • Flash
    看了很多评论后,发现思考题其实就是举一反三,我们可以在比较时,将二维串矩阵看作是字符串来处理,至于怎么转换成一维字符串,应该有很多方法,比如子串矩阵和模式串矩阵都用同样的规则来组成一个字符串,从左到右,再从上到下遍历取矩阵的元素a[i][j]。转换为一维字符后,就可以用BF或者RK算法了 。
    复杂度分析,假设二维主串矩阵和模式串矩阵的维度分别是 m*n 和 i*j,按一个个矩阵来看子串的话,共有(m-i+1) * (n-j+1)个子串矩阵。
    用RK算法的话,复杂度就是O((m-i+1) * (n-j+1))。
    2019-01-27
    1
    12
  • Alan
    h[i] = 26*(h[i-1]-26^(m-1)*h[i-1]) + h[i+m-1];

    其中, h[i]、h[i-1] 分别对应 s[i] 和 s[i-1] 两个子串的哈希值
    ------------------
    文中这个公式,26*(h[i-1]-26^(m-1)*h[i-1])可以化简为26*h[i-1]*(1-26^(m-1)),所以这里是不是应该改为26*(h[i-1]-26^(m-1)*s[i-1]),用s[i-1]代表当前位置的字符串的值,例如图中d的值是3,同样的公式后面加 h[i+m-1]是不是也是s[i+m-1]呢

    作者回复: 写错了 感谢指正 已经改了

    2018-12-05
    1
    10
  • coulson
    老师,前天面试被问到一个问题,关于地图算法的,比如线路推荐。请问地图算法会讲到么

    作者回复: 在高级篇那部分会涉及一点

    2018-12-05
    1
    7
  • 朱月俊
    以前刷题的时候,遇到过rk算法,当时是没太考虑hash冲突,一个字母对应一个数字,子串的hash值就是子串中的字母对应的数字想加。
    今天大佬将之抽象提炼出来,还专门提到冲突解决方法,不可谓不妙!
    2018-12-05
    7
  • yaya
    二维矩阵只要如果以i'j为左上角,即可定义一个i.j i+1.j i. j+1 i+1.j+1的子串,其本质是和字符串相同的,即可以由rf又可以由bf解
    2018-12-05
    6
  • 蒋礼锐
    思考题:
    可以先查找第一行的字符串,假设长度为m,用bf或者rk都可以,假设是n*n的数组,
    bf的复杂度是(n-m)*n
    rk的复杂度为n

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

    最坏时间复杂度为
    bf:(m-n)^2*n^2
    rk:n^2

    但是如果第一行不匹配的话是不会进行第二行的匹配的,平均复杂度会小很多。
    2018-12-05
    5
  • 煦暖
    “h[i] = 26*(h[i-1]-26^(m-1)*h[i-1]) + h[i+m-1];其中, h[i]、h[i-1] 分别对应 s[i] 和 s[i-1] 两个子串的哈希值。”老师你好,26^(m-1)*h[i-1]中的h[i-1]和h[i+m-1]应该是一个字符的哈希值而不应该是子串的哈希值吧??PS:用您的例子套用公式:h0 = 3*26*26+1*26+2 = 2056;h1=26*(h0-26*26*h0)+(4*26*26+3*26+4) = -36080014 和期望的哈希值1*26*26+2*26+4=732不符。

    2018-12-05
    1
    4
  • 晓龙
    RK算法有两个可以改进的点,一个可以避免hash冲突,另一个可以减少hash计算次数。

    改进一:先计算模式串的hash值,记录下来,然后计算每一个子串的hash,计算一次,就对比一次,如果hash值匹配,在全量对比字符串。这样做可以不用关心hash冲突问题。

    改进二:计算子串hash值的时候只要计算到n-(n-m)处即可,剩下的子串长度小于模式串,不用计算。
    2019-03-01
    3
  • Rephontil
    h[i] = 26*(h[i-1]-26^(m-1)*(s[i-1]-'a')) + (s[i+m-1]-'a');
    其中, h[i]、h[i-1] 分别对应 s[i] 和 s[i-1] 两个子串的哈希值
      好吧,这一段代码我对照上下文看了10遍,看懂了。我承认我很蠢

    作者回复: 抱歉 没写清楚

    2019-01-09
    3
  • Rephontil
    老师您应该把公式的推理过程简单地说一下,这公式对您来说非常简单,但是对于我这种基础差的人,完全是懵逼的状态。看着大家讨论,却无法深入下去。/(ㄒoㄒ)/~~

    作者回复: 我补一下

    2019-01-09
    3
  • 星君
    好几期没看了,感觉跟不上了

    作者回复: 不着急 慢慢来 没必要一直紧跟着

    2018-12-12
    3
  • hunterlodge
    RK算法和布隆过滤器的思想是一致的
    2018-12-07
    3
  • 王婵
    思考题二维数组用RK算法计算哈希值要复杂一些,i-1 j-1不越界的情况下,如果模式串的行数大于列数可以通过h[i-1,j]计算h[i,j],如果列数大于行数可以通过h[i,j-1]计算h[i,j] 还有更好的方法计算哈希值吗?
    另外正文里的计算哈希值的公式貌似写错了,应该是
    h[i] = 26*(h[i-1]-26^(m-1)*(s[i-1]-‘a’)) + (s[i+m-1]-‘a’);
    2018-12-05
    3
  • qinggeouye
    RK 算法,计算相邻两个子串哈希值的规律,比如,相邻两个子串分别为 "dbc" 和 "bce",那么 s[i-1] = 'd',s[i]='b' ,与 'a' 相减意思是,它们的 ASCII 码值的差值正好表示字母的数值;
    评论中有同学不明白老师给的哈希值 h[i] 和 h[i-1] 之间的关系,我想应该是 s[i] 和 s[i-1] 代表什么这点没明白。
    2018-12-31
    2
  • 大刚
    老师,下面的不懂,能不能在详细讲解下
    h[i] = 26*(h[i-1]-26^(m-1)*(s[i-1]-'a')) + (s[i+m-1]-'a');
    2018-12-20
    2
  • 漫漫越
    停看了好多天,最后还是决定继续跟随老师的脚步,为自己加油~

    作者回复: 停一停 歇歇挺好的 也没必要跟的很紧 搞的自己很窘迫

    2018-12-18
    2
收起评论
99+
返回
顶部