KMP字符串匹配算法

这两天仔细研究了一下kmp匹配算法,一开始觉得有点绕,后来一步步把序列写到纸上比较,终于算是搞懂了这里面的逻辑,写出来和大家分享一下。

一、kmp算法描述

kmp算法旨在高效地从字符串S中找到子串T的位置。对于这样一个问题,最容易想到的做法便是:遍历S,对于S中的每一个字符,遍历T,并逐个比较S和T中的字符,直到找到完全匹配的位置。这种做法完全没有利用前一次错误匹配中的信息。而kmp算法通过分析子串T的结构,使得在匹配错误之后,不用在S中回溯,也不用再次从T的第一个字符开始比较,从而实现了算法效率的提升。

kmp算法使用一个与T等长度的next数组来表征T的结构信息。对于字符串T:

image

字符pj的next[j]值定义为

image

 

next[j]的意义就是字符pj之前字符串p0p1…pj-1中,前缀子串恰与后缀子串相等的最大长度。例如:字符串”abcdabcefgh”的next数组即为[-1 0 0 0 0 1 2 3 0 0 0]。

有了子串T相应的next数组之后,kmp算法首先依然是从首个字符串开始匹配S和T,假设S和T的前j个字符均可匹配,但第j+1个字符不匹配,即image,如下图

image

此时按照kmp算法,只需要将T移动到上图所示的位置,并且从imageimage开始向后匹配即可,其中k=next[j]。后面以此类推,这样便不需要在S串中回溯,也不需要从T串的第一个字符开始匹配,大大提高了算法效率。

这里有两个问题:首先,为何不用比较T中的image与S中的image?这点比较简单,由T串的next[j]=k的定义即可得出这二者必定相等。第二,为何p0可以直接移动到pj-k的位置,而之前的位置都不用比较?这里采用反证法,假设将T中的p0移动到S中pj-k-1的位置,如下图

image

可以判定S中image与T中的image必定不相等,因为如果相等的话,就可以得出T串中next[j]=k+1,矛盾,所以此位置不需要比较就可以判定不相等。以此类推,pj-k之前的位置都不需要比较。

C语言的kmp算法代码如下:(其中get_next函数后面描述)

int kmp_index(char* S, char* T)
{
    int T_len = strlen(T);
    int* next = (int *)malloc(T_len * sizeof(int));
    get_next(T, next);
    int i = 0, j = 0;
    while (S[i] != '\0') {
        if (S[i] == T[j] || j == -1) {
            if (j == T_len - 1) { return i-j; }
            ++i;  ++j;
        } else {
            j = next[j];
        }
    }
    return -1;
}

二、计算next数组

上面我们知道,kmp算法的关键在于计算T串的next数组的值,这里我们采用递推的方法,假设已知next[j]=k,如何求next[j+1]呢?

由next[j]=k可知:T串中有p0p1…pk-1 = pj-k…pj-1。接下来看pk与pj

(1)若pk=pj,那么就有p0p1…pk-1pk= pj-k…pj-1pj  也就是next[j+1] = next[j]+1=k+1

(2)如果pk不等于pj,采用回溯的方法:

此时我们需要找一个k1,使得image,这样即可说明next[j+1]=k1。但是如何找到这样一个k1呢?这就是回溯的关键。

如果image,则必有

image

而由next[j] = k >= k1可知:

image

取红色方框的部分,并与上面大括号的方程联立,可以得到:

image

上面的式子表明:k1=next[k],这就是回溯表达式,检查k1对应的字符与pj是否相等,若相等则next[j+1]=k1, 否则接着按照回溯表达式回溯,直到next[k]=-1为止,也就是回溯到T串的第一个字符。

按照上面的逻辑,可用C语言描述get_next函数如下:

void get_next(char* T, int* next)
{
    next[0] = -1;
    int j = 0;
    int k;
    while(T[j] != '\0') {
        k = next[j];
        while (T[j] != T[k] && k != -1) {  // 若不相等,回溯找k1,直到相等或到达第一个为止
            k = next[k];
        }
        next[j+1] = k+1;
        j++;
    }
}

posted on 2013-11-23 13:31  hunter7z  阅读(237)  评论(0)    收藏  举报