腾讯和阿里的笔试刚过去了,里面有不少题都很值得玩味的。以前Blog积累的不少东西,还要平时看的书,都有很大的帮助。这个深有体会啊!html
例如,腾讯有一道算法题是吃香蕉(好邪恶的赶脚..),一次吃一根或者两根,50根香蕉能够有多少种吃法?当时我一看尼玛,不就是我以前总结过的:递归算法,JavaScript实现。里面的走楼梯的问题,我到如今仍是记得的。(可是为了抗议我对卷纸的不专业性,我用CoffeeScript实现了算法...感受可能会所以跪下。)而后就是有一道选择题,考的是Javascript的闭包陷阱,我一看尼玛,不是我以前总结过的:循环闭包的影响以及其解决方案。我也是如出一辙用setTimeout去模拟的。简直不能再爽。固然,也不得不说,腾讯到最后也只有这两题和前端有一点联系。前端
相比之下,阿里就好不少了。虽然时间很紧,题目不少,但起码不会一抬眼全是熟悉的陌生人。印象比较深的是《Javascript设计模式》里的观察者模式,还有《Javascript高级程序设计》里的有关CookieUtil的。。可是,我有一题,彻底不记得如何作了。那就是今天的主角,KMP算法!算法
上面扯淡完毕了。我的博客嘛,为所欲为啦。先给参考资料的地址:字符串匹配的KMP算法。这个是阮一峰老师的博文,算是写的很不错的了。想看生动形象的博文的同窗能够直接移步过去。设计模式
那这个用于字符串匹配的KMP算法到底怎么用的呢。咱们先看看需求:字符串A="BBCABCDABABCDABCDABDE"里如何快速匹配到a=“ABCDABD”。用伪代码来写这些步骤应该是这样的:闭包
问题来了:子字符串回滚到哪儿?如果回滚到匹配开始的下一位,那固然是能够的,只不过是作了不少的无用功。因此KMP算法就是为了这个时候诞生的,能够有效的提升效率。优化
这里我用阮老师的一张图更好的解释一下。
咱们能够看到,最佳的回滚位置应该是让子字符串的“C”对应空格。这样咱们才能够最优化的处理重复的“AB”这个东西。设计
直接看一个公式:回滚位数 = 已匹配的字符数 - 对应的部分匹配值
。咱们能够看到已经匹配的字符数是6,而后最佳的回滚位数是4,那么对应的部分匹配值应该是2,那这个2是怎么来的?code
这就是KMP算法的精华。对于一个字符串:“ABCDABD”htm
* "A"的前缀和后缀都为空集,共有元素的长度为0; * "AB"的前缀为[A],后缀为[B],共有元素的长度为0; * "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0; * "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0; * "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1; * "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2; * "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
因此咱们最终只要观察到共有元素的最大长度,便可使用公式。那咱们要实现这个算法,就要取得部分匹配表的算法和回滚算法。那咱们看一下该如何实现。blog
var kmpGetPartMatchLen = function(str){ var partMatch = []; for(var i = 0; i < str.length; i++){ var prefix = "", suffix = ""; var newStr = str.slice(0, i + 1); if(newStr.length <= 1){ partMatch[i] = 0; }else{ //判断先后缀是否相同 for(var j = 0; j < i; j++){ prefix = newStr.slice(0, j + 1); suffix = newStr.slice(-j - 1); //利用负参数尾巴开始取 if(prefix === suffix){ partMatch[i] = prefix.length; } } //不存在检测 partMatch[i] = partMatch[i]? partMatch[i] : 0; } } return partMatch; };
上面这个是取出部分匹配表的算法的实现,而后接下来就是回滚算法的实现。
var kmp = function(sourceStr, subStr){ var partMatch = kmpGetPartMatchLen(subStr); var result = false; for(var i = 0; i < sourceStr.length; i++){ for(var j = 0; j < subStr.length; j++){ if(subStr.charAt(j) === sourceStr.charAt(i + j)){ if(j === subStr.length - 1){ result = true; break; } }else{ //实现回滚,以subStr为参照物,即sourceStr往前移动 if(j > 0 && partMatch[j-1] >= 0){ //公式在此处实现 i += (j - 1 - partMatch[j-1] - 1); }else{ break; } } } if(result) break; } if(result){ return i; }else{ return -1; } };
那回到咱们的笔试题,要实现手机号后四位在π中匹配的位置,那如今就是一句话的事情啦!
var π = "3.1415926.........." kmp(π, "1092");