![82f77438e780843fec5a705b589d34fc.png](https://i-blog.csdnimg.cn/blog_migrate/c7720a95fd4d4367db3eb453c030488e.jpeg)
说明
以下算法介绍中,被匹配字符串称为主串,匹配模式字符串称为匹配串,索引从0开始。
前缀数组:字符串S = AB(B !== ⏀,即B为任一非空字符串) ,S的前缀指A。前缀数组指所有包含第一个字符但不包含最后一个字符的子串集合。
后缀数组:字符串S = AB(A !== ⏀,即A为任一非空字符串) ,S的后缀指B。后缀数组指所有包含最后一个字符但不包含第一个字符的子串集合。
一、BF
Brute Force 算法,简称BF。基本思想就是将主串第pos个字符与匹配串的第一个字符进行比较,若相等,继续将主串的下一个字符与匹配串的下一个字符进行比较,若不相等,从主串的pos + 1 位置开始与匹配串的第一个字符进行比较,重复前面的比较模式,直到得到结果。
假设主串长度为N,匹配串长度为M,该算法最坏的情况下需要进行 M*(N-M) 次比较,算法时间复杂度为O(M*N)。
/**
二、KMP
Knuth-Morris-Pratt 算法,简称KMP,由 Donald Knuth,James H. Morris,Vaughan Pratt 1977年联合发表。
BF 算法效率低在于每轮主串从pos位置开始比较过程中,若字符不相等,主串会回退到 pos+1 位置,然后与匹配串第一个位置字符开始比较。KMP 算法首先算出一个next 数组,匹配串每轮匹配在 j 位置失配时,匹配串向右滑动的距离为 j - next[j]。next 数组的计算规则如下。其中 j 为匹配串索引:
1、j = 0 时,next[j] = -1;
2、j > 0,匹配串0至 j-1 位置的子串前缀和后缀数组中最长相同元素的长度为maxL,则next[j] = maxL。特殊的,当 j=1时,前缀和后缀都为空,所以maxL=0,next[1] = 0;
/**
* @name kmp
* @param <String> s 主串
* @param <String> m 匹配串
* @return <Number> pos 返回匹配串在主串第一次出现的位置,若不存在,返回-1
*/
function kmp(s = '', m = '') {
const lenS = s.length
const lenM = m.length
let j = 0
if(!lenS || !lenM || lenS < lenM) {
return -1
}
const next = getNext(m)
for(let pos = 0; pos < lenS - lenM + 1; pos++) {
while(j < lenM && s[pos + j] === m[j]) {
j++
}
// 匹配完成
if(j === lenM) {
return pos
}
// 匹配不完全,匹配串从next[j] 位置开始匹配
j = next[j]
}
return -1
}
/**
* @name next
* @param <String> m 需要求出next数组的匹配串
* @return <Array> 返回next 数组
*/
function getNext(m = '') {
const len = m.length
const next = new Array(len).fill(0)
next[0] = -1
// 保存前缀和后缀数组最长相同子串
let k = 0
for(let j = 1; j < len - 1; j++) {
while((k > 0) && (m[j - 1] !== m[k])) {
k = next[j-1]
}
if((j > 1) && (m[j - 1] === m[k])) {
k++
}
next[j] = k
}
return next
}
二、BM
Boyer-Moore 算法,简称BM,由Robert S. Boyer 和 J Strother Moore 1977年发明。
当在 i 位置字符失配,进行下一轮匹配时,BF算法主串回退到 i+1 位置,匹配串回退到0位置。而KMP 算法主串不回退,匹配串自0位置向右滑动一定距离再进行比较。显然,匹配串向右滑动的距离越长,算法越高效。BM 算法从末尾开始匹配,根据以下两条规则,计算得出的两个数字取最大的一个为最终滑动距离:
1、坏字符规则
![926b84beac2476c981fa2978d371bda5.png](https://i-blog.csdnimg.cn/blog_migrate/4d268e0544a1bf9569c705470326c05c.jpeg)
如上图,匹配过程中,m 位置 j 的 c字符不等于a,所以c是坏字符。这时有两种情况:
i、如果匹配串m的坏字符c左侧位置 i(i < j)存在字符等于坏字符对应主串中的a字符,则匹配m向右滑动距离为 j-i,即将位置 i 的字符与当前坏字符对应主串的字符对齐。
ii、如果匹配串m的坏字符左侧不存在字符等于坏字符对应的主串中的字符,则m向右滑动距离为 j+1。
2、好后缀规则
![18c2e38c67ff3e2876f9dc0835404d95.png](https://i-blog.csdnimg.cn/blog_migrate/c34cffded85e5d9ab4cea16663ebc726.jpeg)
如上图,匹配串从末尾开始匹配,匹配到m中的c不等于s中的a,则c是坏字符,而"ba"的所有子串都是好后缀,设为"ab"为 t,好后缀最大的索引为 i,即a 索引为i。这时也有两种情况:
i、如果m的t左侧存在子串 t' 使得 t'===t,则滑动m使得 t‘ 与 s中的 t 对齐。如果 t' ,则查找是否在 t 的某一个后缀 t2 左侧存在子串 t3 等于t2 ,则滑动m 使得t3 与s 中对应的t2 对齐。
iii、如果匹配串m 的好后缀左侧都不存在符合第一种情况的子串,则整个匹配串向右滑动距离为 i+1。
后记
这篇文章感觉要烂尾了呀,时间线拖太长了。
很珍惜能来到tx 的机会,也很认真地想把工作做好,反而如履薄冰,战战兢兢。还是喜欢洒脱的自己,只管全力以赴,其他的,不问、不求、不急,花开花谢,都是风情。