BM算法
模式串最好不要太长(因为预处理较重),比如IDE编辑器里的查找场景;预处理O(m*m),匹配O(n),实现较复杂,需要较多额外空间。
坏字符规则
从后往前逐位比较模式串与主串的字符,当找到不匹配的坏字符时,记录模式串的下标值 si ,并找到坏字符在模式串中,位于下标 si前的最近位置 xi (若无则记为-1),si - xi 即为向后滑动距离。 但是坏字符规则向后滑动的步幅还不够大,于是需要好后缀规则。
将模式串中的每个字符及其下标都存在 哈希表中,这样可以快速找到坏字符在模式串的位置下标。相同的模式串字符,仅记录最后的位置。
好后缀规则
从后往前逐位比较模式串和主串的字符,当出现坏字符时停止。 若已经存在匹配成功的子串 {u}, 那么模式串的 {u} 前面找到最近的 {u},记为 {u’}。 再将模式串后裔,是的模式串的 {u’} 与主串的 {u} 重叠。
若不存在 {u’},则直接把模式串移到主串的 {u} 后面。
为了没有遗漏,还需要找到最长的、能够跟模式串的最长前缀子串匹配的,好后缀的后缀子串(同时也是模式串的后缀子串)。然后把模式串向后移动到其左边界,与这个好后缀个后缀子串在主串的中的左边界对齐。
好后缀的处理规则中最核心的内容:
- 在模式串中,查找跟好后缀匹配的另一个子串;
- 在好后缀的后缀子串中,查找最长的、能跟模式串前缀子串匹配的后缀子串。
技巧:
每次执行好后缀原则时,都会计算多次能够与模式串前缀子串相匹配的好后缀的最长后缀子串。为了提高效率,可以预先计算模式串的所有后缀子串,在模式串与之匹配的另一个子串的位置。同时预计算模式串中(同长度)后缀子串与前缀子串是否匹配并记录。在具体操作中直接使用,大大提高效率。
如何快速记录模式串后缀子串匹配的另一个子串位置,以及模式串(相同长度)前缀与后缀子串是否匹配呢? 先用一个suffix数组,下标值k为后缀子串的长度,从模式串下标为
i (0 ~ m-2)的字符为最后一个字符,查找这个子串是否与后缀子串匹配,若匹配则将子串起始位置的下标值j赋给suffix[k]。 若j为0,说明这个匹配子串的起始位置为模式串的起始位置,则用一个数组prefix,将prefix[k]设为true,否则设为false。k从0到m(模式串的长度)于是就得到了模式串所有前缀与后缀子串的匹配情况。
Java代码
1 | package com.ludepeng.datastruct.base.datastruct.charMath.diffculty.bm; |
1 | package com.ludepeng.datastruct.base.datastruct.charMath.diffculty.bm; |
总结
- 对于一段代码不理解,可以使用Test进行debug,看看如何执行的,极大提高对这段代码执行流程的理解!!!!
比如生成suffix和prefix数组的这段代码的测试
1 | package com.ludepeng.datastruct.base.datastruct.charMath.diffculty.bm.demo; |
1 | package com.ludepeng.datastruct.base.datastruct.charMath.diffculty.bm.demo; |