我來寫KMP算法

KMP算法是(Knuth-Morris-Pratt)提出的一種算法,其核心思想是通過分析子串來實現串匹配過程中不回溯。這樣對於高頻查找,還有長字串查找十分有幫助。
首先我們來看看普通的回溯查找:
樸素的查找算法最讓人頭疼的就是回溯,一旦一次匹配不成功,主串 字串都要回溯,主串回溯到到以前的位置的下一個位置,字串直接回溯到開始處。

所以我們可以計算其時間複雜度爲: 最壞情況O(mn)其中m爲主串長度,n爲字串長度。
其實這是一個很可怕的複雜度,比如我們來做一下假設: 如果我們從一本書中查找一句話,假設這本書有1,000,000個負號構成,每個符號佔用1個byte,所以就是1M個字節,我們來查找一句話,這句話是這樣的:
The design of the Knuth-Morris-Pratt algorithm follows a tight analysis of the Morris and Pratt algorithm. Let us look more closely at the Morris-Pratt algorithm. It is possible to improve the length of the shifts.
呵呵,這是KMP算法的一個描述,起碼得100個字符吧,算算複雜度~當然並不是每次都處於最壞的情況。

正如上面那句話所說的: KMP算法它可以解決這個問題,使得複雜度下降:
preprocessing phase in O(m) space and time complexity;
searching phase in O(n+m) time complexity (independent from the alphabet size);
呵呵,也就是處理字串的時間是 線性的,同樣查找也是線性的。
讓我們來看看什麼是KMP算法:
首先我們來分析一下不匹配的情況:

如圖所示,當匹配進行到e 跟字串中的d時,匹配過程中被中斷了,遇到了不匹配的情形。但是由一個有趣的現象,就是主串中的綠色部分跟字串中的綠色部分是一樣的。
所以這就向我們透漏一個信息:我們可以通過分析字串來確定到底回溯多少,還是直接就不回溯!
這一點是很重要的一點,也是算法的根本出發點。所以下面的分析將主要集中在字串的分析,畢竟主串就是子串嘛!


我們來分析子串什麼呢?呵呵當然是在什麼情況下可以子串自己根自己部分匹配。對於上面的那個子串abcabcd來說
藍顏色下劃線部分是一個很好的現象,爲什麼呢,因爲從普通的匹配算法來說,對於子串是要回溯到開始部分的,如果我們已知開始部分跟主串中的某個部分匹配,其他全都不匹配,那麼我們只要處理一下這種情況就好了。當然這個某個部分也不是亂選的,必須是已匹配子串的尾部,有點繞口,就是途中綠顏色的主串的尾部。
爲什麼呢?如果不是尾部我們有一個匹配,那麼尾部肯定不匹配,說明這樣的匹配不能進行下去,所以必須是尾部。
                        尾部有什麼好處----主串不用回溯
看看人家大師的定義:
Consider an attempt at a left position j, that is when the the window is positioned on the text factor y[j .. j+m-1]. Assume that the first mismatch occurs between x[i] and y[i+j] with 0 < i < m. Then, x[0 .. i-1] = y[j .. i+j-1] =u and a = x[i]  y[i+j]=b.

When shifting, it is reasonable to expect that a prefix v of the pattern matches some suffix of the portion u of the text. Moreover, if we want to avoid another immediate mismatch, the character following the prefix v in the pattern must be different from a. The longest such prefix v is called the tagged border of u (it occurs at both ends of u followed by different characters in x).

上面羅力巴索就是這個意思:
        對於每一個子串的字母,都有一個next value, 這個value的是這樣定義的:
                 -1   if j ==0 
        next[j]= k  k= max m where  a[0]...a[m-1] = a[j-m-1]...a[j-1]
                 0    其他
這個就是算法的精華,呵呵有了這個next——value之後我們就不用在主串回溯了,只是調整一下子串就可以了。
                        a[0]...a[m-1] = a[j-m-1]...a[j-1]
這句話就是我們說的開始部分跟尾部匹配.
我們來看看如何求這個next_value吧。
        假設我們已經有一個串:
                abcabeabcabcd

如果我們求解模式串的時候已經進行到d,呵呵前面的已經求解完成,那麼我們只要判斷c(d的前一個字母)是否跟其next_value是否相等就可以了,如果相等呢,說明其匹配串又長了一個, 呵呵c的next_value +1 如果不相等呢繼續向前回溯,呵呵這個過程是不是很想主串中匹配子串呢??
^_^基本上就這樣了。
下面是源程序:C/C++
int Kmp(const char *s, const char*d, int p, int *next )
{
        if (NULL == s || NULL == d || NULL == next) return -1;
       
        int sl = strlen(s);
        int dl = strlen(d);
        int j =0;
        while(j<dl && p < sl){
                if(s[p] == d[j] || -1 == j){
                        ++p;++j;
                }
                else{
                        j = next[j];
                }
        }
        if(j>=dl) return p;
        else return -1;
}

void getnext(const char *s,int *next)
{
        int sl = strlen(s);
        int j = 0;
        int i = -1;
        next[0] =-1;
        while( j < sl){
                if(-1 ==i || s[j] == s[i]){
                        ++i;++j;
                        next[j] = i;
                }
                else
                        i =next[i];
        }
        return ;
}

兩個網址很有用,
http://www-igm.univ-mlv.fr/~lecroq/string/node8.html
http://www.cee.hw.ac.uk/~alison/ds98/node78.html

 

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