KMP算法用JavaScript實現

KMP算法是字符串匹配的經典算法,簡稱 看毛片, 理論知識請直接看阮一峯老師的這篇文章,我看完文章以後嘗試對算法進行了實現。html

一句話總結KMP算法的核心思想:就是跳過已經對比的部分算法

而KMP算法的核心組成就是部分匹配表 + 回退算法數組

部分匹配表1.0版本app

            function KMPpartMatchTable(str) {
                var matchTable = [0];
                var prefix = [],
                    suffix = [];
                for(var i = 1; i < str.length; i++) {
                    prefix = getPrefix(str.substr(0, i + 1))
                    suffix = getSuffix(str.substr(0, i + 1))

                    var ret = [0]; //默認設置一個0,防止-Infinity
                    //對比2個數組,是否有重複的
                    prefix.forEach(function(n, i) {
                        for(var j = i; j < suffix.length; j++) {
                            if(n == suffix[j]) {
                                ret.push(n.length)
                            }
                        }
                    })
                    matchTable.push(Math.max.apply(null, ret))
                }
                //生成前綴數組
                function getPrefix(s) {
                    let ret = []
                    for(var len = s.length; len > 0; len--) {
                        if(len == s.length) continue;
                        ret.push(s.substring(0, len))
                    }
                    return ret.reverse();
                }
                //生成後綴數組
                function getSuffix(s) {
                    let ret = []
                    for(var len = s.length; len > 0; len--) {
                        if(len == s.length) continue;
                        ret.push(s.substring(len, s.length))
                    }
                    return ret.reverse();
                }
                return matchTable
            }

這是我初版寫出來的,能夠看到2個getPrefix和getSuffix有大部分是重複的代碼。方便理解。須要for循環2次字符串,但不利於性能。因此能夠將他們進行精簡合併爲1次性能

部分匹配表2.0版本spa

            function KMPpartMatchTable(str) {
                var matchTable = [0];
                var prefix = [],
                    suffix = [];
                for(var i = 1; i < str.length; i++) {
                    // prefix = getPrefix(str.substr(0, i + 1))
                    // suffix = getSuffix(str.substr(0, i + 1))
                    var s = str.substr(0, i + 1);
                    for(var len = s.length; len > 0; len--) {
                        if(len == s.length) continue;
                        prefix.push(s.substring(0, len)) //前綴數組
                        suffix.push(s.substring(len, s.length)) //後綴數組
                    }
                    
                    var ret = [0]; //默認設置一個0,防止-Infinity
                    //對比2個數組,是否有重複的
                    prefix.forEach(function(n, i) {
                        for(var j = i; j < suffix.length; j++) {
                            if(n == suffix[j]) {
                                ret.push(n.length)
                            }
                        }
                    })
                    matchTable.push(Math.max.apply(null, ret))
                }
                return matchTable
            }
            KMPpartMatchTable('ABCDABD')//[0,0,0,0,1,2,0]
 

改進事後,邏輯沒那麼直觀了。但一次字符串for循環就生成出了前綴和後綴數組code

 

回退算法htm

            function KMP(sourceStr, targetStr) {
                var partMatchValue = KMPpartMatchTable(targetStr); //拿到匹配表
                var result = false;
                for(var i = 0; i < sourceStr.length; i++) {
                    for(var k = 0; k < targetStr.length; k++) {
                        if(str.charAt(k) == sourceStr.charAt(i)) {
                            if(k == targetStr.length - 1) {
                                result = true;
                                break;
                            } else {
                                i++;
                            }
                        } else {
                            if(k > 0 && partMatchValue[k - 1] > 0) {
                                k = partMatchValue[k - 1] - 1;
                            } else {
                                break;
                            }
                        }
                    }
                    if(result) {
                        break;
                    }
                }
                return result
            }
            
            var ss = 'ABCDAB ABCDAB ABCDAABCABCDABDABCDABDDABDBD'
            var str = 'ABCDABD'
            console.log(KMP(ss, str)) //true
相關文章
相關標籤/搜索