BF算法即暴力匹配算法,在主串中,检查起始位置分别是0、1、…、n-m且长度为m的n-m+1个子串,看有没有和模式串匹配的,时间复杂度是$O(mn)$。虽然时间复杂度比较高,但在实际开发中比较常用,因为实际开发中模式串和主串的长度都不会太长,而且每次匹配不会遍历m个字符,在中间不同就会停止,所以执行效率要比$O(mn)$高很多。其次,这种算法思想简单,实现也简单,有bug容易暴露和修复,符合KISS(Keep it Simple and Stupid)设计原则,即在满足性能要求的前提下,简单是首选。
const SIZE = 256; //坏字符散列表的大小 let bc = Array(SIZE).fill(-1);//坏字符散列表,初始化为-1 functiongenerateBC(pattern, bc) { for(let i = 0; i < pattern.length; i++) { let ascii = pattern.charCodeAt(i);//模式串每一位的ASCII码 bc[ascii] = i; } } functionbm(str, pattern) { const n = str.length, m = pattern.length; generateBC(pattern, bc);//构建坏字符散列表 let suffix = Array(m), prefix = Array(m); generateGS(pattern, suffix, prefix); let i = 0;//表示主串与模式串对齐的第一个字符 while(i <= n - m) { let j; for(j = m - 1; j >= 0; --j) {//模式串从后往前匹配 if(str[i + j] !== pattern[j]) break; //坏字符对应模式串的下标是j } if(j < 0) return i;//匹配成功,返回主串与模式串第一个匹配的字符的位置 let x = j - bc[str.charCodeAt(i + j)], y = 0; if(j < m - 1) { y = moveByGS(j, m, suffix, prefix); } i = i + Math.max(x, y); } return-1; } functionmoveByGS(j, m, suffix, prefix) { let k = m - 1 - j; if(suffix[k] !== -1) return j - suffix[k] + 1; for(let r = j + 2; r <= m - 1; r++) { if(prefix[m - r]) return r; } return m; }
KMP
算法实现
//生成next数组 functiongenerateNexts(b) { let k = -1, next = [-1]; for(let i = 1; i < b.length; i++) { while(k !== -1 && b[k + 1] !== b[i]) k = next[k]; if(b[k + 1] === b[i]) k++; next[i] = k; } return next; } functionkmp(a, b) { let next = generateNexts(b); let j = 0; for(let i = 0; i < a.length; i++) { while(j > 0 && a[i] !== b[j]) j = next[j - 1] + 1; if(a[i] === b[j]) j++; if(j === b.length) return i - b.length + 1; } return-1; }
Trie
特点:
根节点不包含字符,除根节点外的每一个子节点都包含一个字符
从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
每个节点的所有子节点包含的字符互不相同
优点:
插入效率和查询的效率很高,都为$O(m)$,其中m是字符串长度
树中不同关键字不会冲突
缺点:
空间消耗很大
对缓存不友好
当hash函数很好时,trie树的查找效率会低于哈希搜索
应用:
字符串检索
词频统计
字符串排序
前缀匹配
自动输入补全
Trie的实现
/** * Initialize your data structure here. */ var Trie = function() { this.trie = {}; };
/** * Inserts a word into the trie. * @param {string} word * @return {void} */ Trie.prototype.insert = function (word) { var cur = this.trie; for (let i = 0; i < word.length; i++) { if (!cur[word[i]]) { cur[word[i]] = { value: i === word.length - 1 }; cur = cur[word[i]]; } else { cur = cur[word[i]]; if (i === word.length - 1) cur.value = true; } } };
/** * Returns if the word is in the trie. * @param {string} word * @return {boolean} */ Trie.prototype.search = function(word) { var cur = this.trie; for(let i = 0; i < word.length; i++){ if(!cur[word[i]]){ returnfalse; }else { cur = cur[word[i]]; } } return cur.value; };
/** * Returns if there is any word in the trie that starts with the given prefix. * @param {string} prefix * @return {boolean} */ Trie.prototype.startsWith = function(prefix) { var cur = this.trie; for(let i = 0; i < prefix.length; i++){ if(!cur[prefix[i]]){ returnfalse; }else { cur = cur[prefix[i]]; } } returntrue; };
/** * Your Trie object will be instantiated and called as such: * var obj = new Trie() * obj.insert(word) * var param_2 = obj.search(word) * var param_3 = obj.startsWith(prefix) */