javascript實現KMP算法

參考連接

阮一峯老師的字符串匹配的KMP算法
kmp算法詳解(最透徹的沒有之一!)javascript

KMP算法的原理部分,請看阮一峯老師的字符串匹配的KMP算法,這裏主要是代碼實現。html

構建最大長度表

假設給定模式串ABABCABAA,要求出該模式串的最大長度表。java

i 子串 最長先後綴 最長公共先後綴長度
0 A / 0
1 AB / 0
2 ABA A 1
3 ABAB AB 2
4 ABABC / 0
5 ABABCA A 1
6 ABABCAB AB 2
7 ABABCABA ABA 3
8 ABABCABAA A 1

很容易理解最大長度表每一個值的含義是模式串子串str[0,i]的最長公共先後最綴長度。 如今,咱們來回顧一下這個表是怎麼得來的?
假設咱們目前獲得的表是下面這個樣子的,最後兩個是未知的,也是待求解的算法

i 0 1 2 3 4 5 6 7 8
pattern[i] A B A B C A B A A
len 0 0 1 2 0 1 2 ? ?

咱們已經知道i=6的值是2,對應的模式串字符爲A,那麼咱們如何獲得i=7的值呢?數組


咱們只須要將pattern[len]pattern[i]進行比較便可,此時,咱們獲得pattern[len]===pattern[i],而後對len++,記錄prefix_table[i]===len,i指針後移便可。函數

i 0 1 2 3 4 5 6 7 8
pattern[i] A B A B C A B A A
len 0 0 1 2 0 1 2 3 1

如今,咱們已經獲得了i=7的值,還剩下i=8。咱們一樣按照上面的思路來思考。 比較pattern[8]--->Apattern[3]---->B,出現失配的狀況,那此時咱們應該怎麼作呢,咱們的思路是縮短公共先後綴的長度,使prefix_table發生側移。測試

代碼以下:優化

function generatePrefixTable(pattern){
    var prefix_table = [];
    var len = 0;// 最長公共先後綴長度初始化爲0
    prefix_table[0] = len;
    var i = 1;
    var n = pattern.length;
    while(i<n){
        if(pattern[len] === pattern[i]){
            len++;
            prefix_table[i] = len;
            i++;
        }else{
            if(len>0){ // 側移
                len = prefix_table[len-1]; //縮小最長公共先後綴的長度
            }else{ // 側移移動到len=0時,pattern[len]!=pattern[i],即pattern[0]!=pattern[i]
                prefix_table[i] = 0;
                i++;
            }
        }
    }
    return prefix_table;
}
複製代碼

構建next數組

function generateNextArr(prefix_table){
    for(var i=prefix_table.length-1;i>0;i--){
        prefix_table[i] = prefix_table[i-1]
    }
    prefix_table[0] = -1;
}
複製代碼

next數組的含義是除當前字符外的最長公共先後綴的長度。spa

i 0 1 2 3 4 5 6 7 8
pattern[i] A B A B C A B A A
next數組 -1 0 0 1 2 0 1 2 3

好比對於ABABCABAA來講,pattern[2]以前的字符串AB中有長度爲0的公共先後綴,因此next[2]=0;pattern[8]以前的字符串ABABCABA中有長度爲3的公共先後綴。.net

kmp搜索算法的實現

function kmp(str,pattern){
        var prefix_table = generatePrefixTable(pattern)
        generateNextArr(prefix_table)
        var i=0; // str 指針
        var j=0; // pattern指針
        while(i<str.length && j< pattern.length){
            if(str[i]===pattern[j]){
                i++;
                j++;
            }else{
                j = prefix_table[j] // 右移
                if(j===-1){
                    i++;
                    j++;
                }
            }
        }
        if(j===pattern.length){
            return i-j
        }else{
            return -1
        }
    }
複製代碼

算法測試

kmp("bbc abcdab abcdabcdabde", "abcdabd")  // 結果輸出爲15,正確
複製代碼

KMP算法優化

到目前爲止,已經實現了基本的kmp算法,但仍是存在許多優化的地方。

  • 在上面咱們是先構建最大長度表,而後構建next數組,其實咱們能夠直接構建next數組,節約時間和空間。
function generateNextArr(pattern){
        var i = 0;
        var j = -1;
        var next = []
        next[0]=-1
        while(i<pattern.length){
            if(j===-1||pattern[i]===pattern[j]){
                i++;
                j++;
                next[i]=j
            }else{
                j = next[j]
            }
        }
        return next;
    }
複製代碼
  • 對於上面的kmp函數,咱們能夠改寫成下面的形式
function kmp(str,pattern){
        var next = generateNextArr(pattern)
        var i=0; // str 指針
        var j=0; // pattern指針
        while(i<str.length && j< pattern.length){
            if(str[i]===pattern[j] || j===-1){
                i++;
                j++;
            }else{
                j = next[j] // 右移
            }
        }
        if(j===pattern.length){
            return i-j
        }else{
            return -1
        }
    }
複製代碼

本篇完...

相關文章
相關標籤/搜索