串是由零个或多个字符组成的有限序列,又叫作字符串javascript
串的逻辑结构和线性表很类似的,不一样的是串针对是是字符集,因此在操做上与线性表仍是有很大区别的。线性表更关注的是单个元素的操做CURD,串则是关注查找子串的位置,替换等操做。html
固然不一样的高级语言对串的基本操做都有不一样的定义方法,可是总的来讲操做的本质都是类似的。好比javascrript查找就是indexOf, 去空白就是trim,转化大小写toLowerCase/toUpperCase等等java
这里主要讨论下字符串模式匹配的几种经典的算法:BF、BM、KMPgit
BF(Brute Force)算法github
Brute-Force算法的基本思想:算法
从目标串s 的第一个字符起和模式串t的第一个字符进行比较,若相等,则继续逐个比较后续字符,不然从串s 的第二个字符起再从新和串t进行比较。express
依此类推,直至串t 中的每一个字符依次和串s的一个连续的字符序列相等,则称模式匹配成功,此时串t的第一个字符在串s 中的位置就是t 在s中的位置,不然模式匹配不成功app
可见BF算法是一种暴力算法,又称为朴素匹配算法或蛮力算法。ide
主串 BBC ABB ABCF性能
子串 ABC
在主串中找出子串的位置,对应了其实就是javascript的indexOf查找方法的实现了
var sourceStr = "BBC ABB ABCF"; var searchStr = "ABC";
function BF_Ordinary(sourceStr, searchStr) { var sourceLength = sourceStr.length; var searchLength = searchStr.length; var padding = sourceLength - searchLength; //循环的次数 //BBC ABB ABCF =>ABC => 搜索9次 for (var i = 0; i <= padding; i++) { //若是知足了第一个charAt是相等的 //开始子循环检测 //其中sourceStr的取值是须要叠加i的值 if (sourceStr.charAt(i) == searchStr.charAt(0)) { //匹配成功的数据 var complete = searchLength; for (var j = 0; j < searchLength; j++) { if (sourceStr.charAt(i + j) == searchStr.charAt(j)) { --complete if (!complete) { return i; } } } } } return -1; }
BF算法就是简单粗暴,直接把BBC ABB ABCF母串的每个字符的下表取出来与模式串的第一个字符匹配,若是相等就进去字串的再次匹配
这里值得注意:
1:最外围循环的次数sourceLength - searchLength,由于咱们匹配的母串至少要大于等于子串
2:在子串的继续匹配中,母串的起点是须要叠加的(i+j)
3:经过一个条件判断是否彻底匹配complete,BBC ABB ABCF中,咱们在ABB的时候就须要跳过去
上面是最简单的一个算法了,代码上还有更优的处理,好比在自串的匹配上能够采起取反的算法
优化算法(一)
function BF_Optimize(sourceStr, searchStr) { var mainLength = sourceStr.length; var searchLength = searchStr.length; var padding = mainLength - searchLength; for (var offset = 0; offset <= padding; offset++) { var match = true; for (var i = 0; i < searchLength; i++) { //取反,若是只要不相等 if (searchStr.charAt(i) !== sourceStr.charAt(offset + i)) { match = false; break; } } if (match) return offset; } return -1; }
咱们不须要判断为真的状况,咱们只要判断为假的状况就能够了,当子匹配结束后match没有被修改过的话,则说明此匹配是彻底匹配
以上2种方法咱们都用到了子循环,咱们可否改为一个循环体呢?
其实咱们能够看到规律,主串每次都只会递增+1,子串每次匹配也是从头开始匹配,因此咱们能够改为一个while,控制下标指针就能够了
优化算法(二)
function BF_Optimize_2(sourceStr, searchStr) { var i = 0, j = 0; while (i < sourceStr.length) { // 两字母相等则继续 if (sourceStr.charAt(i) == searchStr.charAt(j)) { i++; j++; } else { // 两字母不等则角标后退从新开始匹配 i = i - j + 1; // i 回退到上次匹配首位的下一位 j = 0; // j 回退到子串的首位 } if (j == searchStr.length) { return i - j; } } }
i就是主串的下标定位,j就是子串的下标定位
当主串子串相等的时候,就进入了子串的循环模式,当子循环的次数j知足子串长度时,就验证是彻底匹配
当主串子串不相等的时候,就须要把主串的下标日后移一位,固然i的时候,由于可能通过子串的处理,因此须要i-j+1, 而后复位子串
具体咱们能够看看代码比较
基于BF算法的四种结构,for/while/递归
BF也是经典的前缀匹配算法,前缀还包括KMP,咱们可见这种算法最大缺点就是字符匹配失败指针就要回溯,因此性能很低,以后会写一下KMP与BM算法针对BF的的升级