KMP算法的原理實現

KMP算法是什麼?

KMP算法是D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人們稱它爲克努特—莫里斯—普拉特操作(簡稱KMP算法)。

目的:爲了解決模式串匹配主串的時間複雜度最小,通俗的講,是有一個字符串A(主串),給定另外一個字符串B(模式串),求A串含有B串的位置

在講KMP算法之前,首先要知道前綴跟後綴的定義

前綴:有一個n個字符的字符串,前n個字符都是前綴。

eg:abcabc ,這個字符的前綴有a,ab,abc,abca,abcab ,5個前綴,最長一個是abcab

後綴:有一個n個字符的字符串,後n個字符都是後綴。

eg:abcabc ,這個字符的前綴有c,bc,abc,cabc,bcabc ,5個前綴,最長一個是bcabc

上面兩個例子,有相同並且最長的前後綴是abc

 

KMP有兩個關鍵的地方,解決這兩個,就解決了KMP算法

1.模式串跟主串是怎麼匹配的?

2.爲模式串建立匹配表

 

一,我們先來看第一個問題,模式串跟主串是怎麼匹配的?

假設

A串(主串):ckabawababab

B串(模式串):ababab

我們憑第一感覺,想一下應該是這樣匹配的,首先A串的第一個c跟B串的第一個a比較,發現不同,A串下移一位k,再與B串a比較,發現又不同,繼續A串下移一位a,再與B串a比較,發現相同,這是A,B串同時下移一位,繼續比較...,直到發現A串w跟B串的b不相同,這時A串不移動,B串回到原來的第一位,又重新開始比較,依次類推,最後算得結果ababab

這種方法有一個缺點,就是B串模式串老是重複計算,效率太低。

接下來,看KMP算法是怎麼做的?

一開始,跟之前的做法一樣,直到發現A串w跟B串的b不相同的時候,這時我們需要看B串b之前的相同最長前後綴是什麼,就是分析aba的前後綴,是a,這是我們不必要將B串模式串移動到原點,只需移動到B串第二位b那裏,爲什麼呢?

因爲A串w前面的a等於B串第三位的a,又因爲B串第三位的a跟B串第一位的a是相同前後綴,所以B串第一位的a必然等於A串w前面的a。

依次類推,最後得出結果

 

二,接下來,我們來解決第二個問題,爲模式串建立匹配表

模式串:ababab

我們用一個臨時數組A來存儲模式串的匹配表

首先模式串第一位字符a前面,沒有相同前後綴,即0

表示 A[0] = 0 ,

模式串第二位字符b前面,沒有相同前後綴,即0

表示 A[1] = 0 ,

模式串第三位字符a前面,沒有相同前後綴,即0

表示 A[2] = 0 ,

模式串第四位字符b前面,有相同前後綴a與a,前綴從下標0開始,下標加一,這樣表示相同前後綴的長度

表示 A[3] = 1 ,

模式串第五位字符a前面,有相同前後綴ab與ab,前綴b從下標1開始,下標加一,這樣表示相同前後綴的長度

表示 A[4] = 2 ,

模式串第六位字符b前面,有相同前後綴aba與aba,前綴a從下標2開始,下標加一,這樣表示相同前後綴的長度

表示 A[5] = 3,

所以A數組=[0,0,0,1,2,3]

//創建匹配表
function createPattern() {
    var str = "ababab";
    var prefix = [];
    var subfix = [];
    var patternMatch = [];
    //匹配表
    for (var i = 0; i < str.length; i++) {
        var newstr = str.substring(0, i + 1);
        if (newstr.length == 0) {
            patternMatch[i] = 0;
        } else {
            for (var k = 0; k < i; k++) {
                prefix[k] = newstr.slice(0, k + 1);
                subfix[k] = newstr.slice(-k - 1);
                if (prefix[k] == subfix[k]) {
                    patternMatch[i] = prefix[k].length;
                }
            }
            if (!patternMatch[i]) {
                patternMatch[i] = 0;
            }
        }
    }
    return patternMatch
}

 

//KMP算法完整代碼
function KMP() {
    var str = "aabaabaaa";
    var sourceStr = "bbbadcadcadcaaabaabaaa";
    var prefix = [];
    var subfix = [];
    var patternMatch = [];
    //匹配表
    for (var i = 0; i < str.length; i++) {
        var newstr = str.substring(0, i + 1);
        if (newstr.length == 0) {
            patternMatch[i] = 0;
        } else {
            for (var k = 0; k < i; k++) {
                prefix[k] = newstr.slice(0, k + 1);
                subfix[k] = newstr.slice(-k - 1);
                if (prefix[k] == subfix[k]) {
                    patternMatch[i] = prefix[k].length;
                }
            }
            if (!patternMatch[i]) {
                patternMatch[i] = 0;
            }
        }
    }

    console.log(patternMatch, "匹配表");
    //循環比對
    var j = 0;
    var result =''
    for (var i = 0; i < sourceStr.length; i++) {
        if (sourceStr.charAt(i) == str.charAt(j)) {
            j++; //模式串下標加一
            if (j == str.length) {
                //比對找到,並返回結果的座標
                console.log(i - str.length + 1, "結果");
                result = i - str.length + 1
                return;
            }
        } else {
            j = j - patternMatch[j]; //根據匹配表後移動
            if (j < 0) {
                j = 0;
            }
        }
    }
    return result;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章