• Smallfly
    2018-12-08
    BM 算法分析着实比较复杂,不过按照老师的思路,一步一步走,看懂应该没问题的。但其实有些代码实现细节看不懂关系也不大。我们学算法主要目的是学习算法的思想,能在需要的时候加以应用就好。

    但对于平时工作,几乎不可能遇到,需要自己手写一个字符串匹配算法的场景。那我们还要学,图的是什么?

    我认为文章中值得学习借鉴的思想有:

    1、要有优化意识,前面的 BF,RK 算法已经能够满足我们需求了,为什么发明 BM 算法?是为了减少时间复杂度,但是带来的弊端是,优化代码变得复杂,维护成本变高。

    2、需要查找,需要减少时间复杂度,应该想到什么?散列表。

    3、如果某个表达式计算开销比较大,又需要频繁的使用怎么办?预处理,并缓存。

    (一点拙见,可能文中还有其它优秀的思想,没能 Get 到)
    展开

    作者回复: 👍

     7
     210
  • meng
    2018-12-09
    我对这次课的内容一知半解,于是在网上搜到一个文档,里面的图挺好的,跟大家分享一下:http://www.cs.jhu.edu/~langmea/resources/lecture_notes/boyer_moore.pdf
     2
     59
  • suke
    2018-12-19
    老师以后的代码中的变量能不能起的有意义一些,这样更加方便大家理解代码啊,不要总拿a b c bc这种完全没有意义的名字来命名变量
     5
     54
  • 纯洁的憎恶
    2018-12-10
    大体思路应该是看懂了,不过具体实现和代码细节还需要时间消化。BM算法的核心思想是通过将模式串沿着主串大踏步的向后滑动,从而大大减少比较次数,降低时间复杂度。而算法的关键在于如何兼顾步子迈得足够大与无遗漏,同时要尽量提高执行效率。这就需要模式串在向后滑动时,遵守坏字符规则与好后缀规则,同时采用一些技巧。

    坏字符规则:从后往前逐位比较模式串与主串的字符,当找到不匹配的坏字符时,记录模式串的下标值si,并找到坏字符在模式串中,位于下标si前的最近位置xi(若无则记为-1),si-xi即为向后滑动距离。(PS:我觉得加上xi必须在si前面,也就是比si小的条件,就不用担心计算出的距离为负了)。但是坏字符规则向后滑动的步幅还不够大,于是需要好后缀规则。

    好后缀规则:从后往前逐位比较模式串与主串的字符,当出现坏字符时停止。若存在已匹配成功的子串{u},那么在模式串的{u}前面找到最近的{u},记作{u'}。再将模式串后移,使得模式串的{u'}与主串的{u}重叠。若不存在{u'},则直接把模式串移到主串的{u}后面。为了没有遗漏,需要找到最长的、能够跟模式串的前缀子串匹配的,好后缀的后缀子串(同时也是模式串的后缀子串)。然后把模式串向右移到其左边界,与这个好后缀的后缀子串在主串中的左边界对齐。

    何时使用坏字符规则和好后缀规则呢?首先在每次匹配过程中,一旦发现坏字符,先执行坏字符规则,如果发现存在好后缀,还要执行好后缀规则,并从两者中选择后移距离最大的方案执行。

    技巧:
    1.通过散列表实现,坏字符在模式串中下标位置的快速查询。
    2.每次执行好后缀原则时,都会计算多次能够与模式串前缀子串相匹配的好后缀的最长后缀子串。为了提高效率,可以预先计算模式串的所有后缀子串,在模式串中与之匹配的另一个子串的位置。同时预计算模式串中(同长度的)后缀子串与前缀子串是否匹配并记录。在具体操作中直接使用,大大提高效率。
    3.如何快速记录模式串后缀子串匹配的另一个子串位置,以及模式串(相同长度)前缀与后缀子串石否匹配呢?先用一个suffix数组,下标值k为后缀子串的长度,从模式串下标为i(0~m-2)的字符为最后一个字符,查找这个子串是否与后缀子串匹配,若匹配则将子串起始位置的下标值j赋给suffix[k]。若j为0,说明这个匹配子串的起始位置为模式串的起始位置,则用一个数组prefix,将prefix[k]设为true,否则设为false。k从0到m(模式串的长度)于是就得到了模式串所有前缀与后缀子串的匹配情况。
    展开
     2
     34
  • meng
    2018-12-23
    这篇文章啃了很长时间了,有个问题请教:是否可以不要prefix数组,直接通过suffix[k]==0来判断前缀子串的匹配与否?
     3
     23
  • Jerry银银
    2018-12-07
    曾经一度觉得字符串匹配的几大算法,都是高山仰止的,难以理解。

    但是前阵子受两句话启发,从此以后对字符串匹配问题,至少在战略层面藐视了它:
    1. 善用之前信息(从信息论的角度:消除信息的不确定性,就是引入信息)

    2. 增加效率,在资源有限的情况下,只有想办法少做事情
    展开
     6
     22
  • Fstar
    2019-02-25
    。。。我知道为什么老师说 si-xi 可能是负数了。

    虽然理论上应该是从 si 的位置往前找 xi。但代码实现为了提高效率,使用了哈希表,记录的是不同字符在模式串中“最后出现的位置”,并不是 si 的位置往前查找的第一个位置,所以确实会出现 xi 大于 si 的情况,原来如此原来如此。。。
    awsl。。
     1
     20
  • 五岳寻仙
    2018-12-07
    老师好!今天讲的BM算法确实有点复杂,不过听的时候有熟悉的感觉,似乎跟之前接触过的Boyer Moore算法很像,查了一下才发现原来是同一种算法😂

    在工作中遇到过这样的情况,需要在一个长度为n (比如十亿级)的巨大的主串中查找长度为m(比如几百)的模式串。主串是固定的,从直观上讲,要加快搜索速度,就需要对主串建索引。BWT-FM算法是解决这类问题最经典的算法,刚接触时也是不好理解,但感觉非常神奇,可以将搜索的时间复杂度降到O(m),是我认为最伟大的算法之一。
    
     20
  • Liam
    2018-12-07
    好后缀原则下,最后一种情况为什么移到坏字符后面呢,不能移到好后缀的后面吗?即m+1,而不是j + 1

    作者回复: 你说的对 👍 我改下

    
     17
  • 传说中的成大大
    2018-12-10
    在用一个256的数组 用字符的ascii码做下标 记录该字符出现的位置 如果存在相同字符怎么办呢?之前的会被新的覆盖掉的把!

    作者回复: 是的 就是要覆盖掉 留最大的

    
     12
  • nopsky
    2018-12-07
    讲shuffix的第一个图中shuffix[4] = -1,这个-1怎么来的,不能理解,能不能再讲一下
     6
     8
  • cygnus
    2018-12-08
    generateGS函数里suffix和prefix的赋值应该放到while循环内,即每次k变动时都要赋值。
    另外请问下:好后缀的后缀子串 b[r, m-1],这里的r的初值j+2是怎么得来的啊?

    作者回复: j表示坏字符的下标 好狗追其实下标j+1

     1
     7
  • 杨伟
    2018-12-07
    这个算法用的多么?老师为什么讲解这个算法?

    作者回复: 怎么讲呢 平常不大可能会自己去实现一个bm算法 顶多就用个bf算法。不过bm算法号称最高效的 比如grep命令就是用它实现的 所以有必要讲一下 不然不完整啊 你全当思维训练吧

    
     6
  • niexia
    2019-10-31
    关于 suffix 和 prefix 的判断取值过程,这样可以帮助理解一下
    模式串为:cabcab,所以后缀子串有:
    后缀子串 长度
             b 1
           ab 2
         cab 3
       bcab 4
     abcab 5
    这些后缀子串都有可能是好后缀{u}!(suffix[k] = j,k{u}是长度,j是在模式串中跟{u}相匹配的{u*}的起始位置)
    1. 好后缀是 b => suffix[1] = 2 prefix[1] = false,不是模式串的前缀子串
    2. 好后缀是 ab => suffix[2] = 1 prefix[1] = false,不是模式串的前缀子串
    3. 好后缀是 cab => suffix[3] = 0 prefix[1] = true,是模式串的前缀子串
    4. 好后缀是 bcab => suffix[4] = -1 prefix[1] = false,不是模式串的前缀子串
    5. 好后缀是 abcab => suffix[5] = -1 prefix[1] = false,不是模式串的前缀子串
    从 1,2,3 中可以看出,如果长度更长的匹配了,前面肯定匹配,因为长度长的包含了长度短的,反之不成立。

    这就是结果,然后在看看代码实现:
    是从头开始匹配,依次将 b[0, i] 和 b[0,m-1]匹配。
    在 while 循环里,依次从后面判断是否匹配,都是先取最后一个来判断,如果匹配就往前,并记录更新位置。如果最后一个都不匹配,那肯定不匹配了

    当匹配了 b,就判断 ab是否匹配,接着判断 cab是否匹配... 最后就得到 suffix 和 prefix。
    展开
     3
     5
  • P@tricK
    2018-12-07
    老师,suffix和prefix的赋值那里有BUG,应该在每一次k的变动都要有suffix赋值。

    作者回复: 是的 多谢

    
     5
  • 不凉青年
    2018-12-21
    这块断断续续看了好几天- -
     1
     4
  • Ryoma
    2018-12-13
    跟课程以来觉得最难的一次,也有可能之前使用手机看的原因。总体上,拿手机看了3次,今天在电脑上看了第一次,终于将好后缀那部分理解清晰。学生时代接触的性能较高字符串匹配算法就是KMP,个人感觉BM比KMP更难理解,大家如果有没理解的,还是要多多看,或者拿着笔画一画
    
     4
  • Alan
    2018-12-12
    在计算suffix 数组和 prefix 数组的代码中,第15行的i是不是多余的诶~?
    
     4
  • P@tricK
    2018-12-08
    高票那个留言,是移动m位,不是m+1位。

    这节课细节上小问题有点多,不过瑕不掩瑜,思想重要,细节自己钻研。

    作者回复: 是的

    
     4
  • seniusen
    2018-12-07
    好后缀原则中,最后一种情况,应该是移动 m 位吧,移动整个模式串的长度。

    作者回复: 是的

    
     4
我们在线,来聊聊吧