KMP算法(next數組方法)


KMP算法之前需要說一點串的問題:

字符串:ASCII碼為基本數據形成的一堆線性結構。

串是一個線性結構;它的存儲形式:

typedef struct STRING {

  CHARACTER *head;

  int length;

};

朴素的串匹配算法:

設文本串text = "ababcabcacbab",模式串為patten = "abcac"     其匹配過程如下圖所示。

黑色線條代表匹配位置,紅色斜杠代表失配位置

算法說明:

一般匹配字符串時,我們從目標字符串text(假設長度為n)的第一個下標選取和patten長度(長度為m)一樣的子字符串進行比較,如果一樣,就返回開始處的下標值,不一樣,選取text下一個下標,同樣選取長度為n的字符串進行比較,直到str的末尾(實際比較時,下標移動到n-m)。在普通的匹配中,假如從文本串的第i個字符來開始於模式串匹配。當匹配到模式串的第j位發現失配,即text[i+j] != patten[j]的時候,我們又從文本串的第i+1個位置來重新開始匹配。盡管我們已經知道了好多字符其實根本就匹配不上,我們還是進行了這個過程,這個時候回溯的過程會非常耗費我們的時間。這樣的時間復雜度是O(n*m)

代碼如下:

int search(const char*str,const char *subStr) {
    int strlen = strlen(str);
    int subStrlen = strlen(subStr);
    int i;
    int j;
    for(i = 0;i <= strlen - subStrlen;i++){
        for(j = 0;j < subStrlen;j++){
            if(str[i + j] != subStr[j])
            break;
        }判斷subStrlen是否比較完成 
    }
}

 

KMP算法:

而KMP算法的實質就是,當遇到text[i+j] != patten[j]的時候,但是我們知道模式串中的 0~j-1 位置上的字符已經於i ~ i+j-1位置上的字符是完全匹配的。就不再重新從text[i+1]開始匹配,而是根據next數組的下標找到patten的下標,從那個下標開始匹配。從而時間復雜度為O(m+n)。

例如模式串Patten = "abaabcac"。其next數組如圖所示:

我們可以看到這次的匹配在藍色的c失配了,而c的下標為5,他的next數組的下標為2。因此,下次的匹配不再是從text[1]開始,而是從text[2]開始,這樣就省去了不必要的比較。

代碼如下:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

#include "kmpmec.h"

void getNext(const char *str, int *next);
int KMPSearch(const char *str, const char *subStr);

int KMPSearch(const char *str, const char *subStr) {
    int strLen = strlen(str);
    int subLen = strlen(subStr);
    int *next;
    int i = 0;
    int j = 0;

    if (strLen <= 0 || subLen <= 0 || strLen < subLen) {
        return -1;
    }
    next = (int *) calloc(sizeof(int), subLen);
    if (subLen > 2) {
        getNext(subStr, next);
    }

    while (strLen - i + next[j] >= subLen) {
        while (subStr[j] != 0 && str[i] == subStr[j]) {
            i++;
            j++;
        }
        if (subStr[j] == 0) {
            free(next);
            return i - subLen;
        } else if (j == 0) {
            i++;
            j = 0;
        } else {
            j = next[j];
        }
    }

    free(next);
    return -1;
}

void getNext(const char *str, int *next) {  //得到next數組 
    int i = 2;
    int j = 0;
    boolean flag;

    next[0] = next[1] = 0;  //next數組的前兩個下標一定為零 
    for (i = 2; str[i]; i++) {
        for (flag = TRUE; flag;) {
            if (str[i-1] == str[j]) { //通過比較失配位置的前一個和前一個下標元素的比較,獲取next數組的下標。
                next[i] = ++j;
                flag = FALSE;
            } else if (j == 0) {
                next[i] = 0;
                flag = FALSE;
            } else {
                j = next[j];
            }
        }
    }
}

int main(void) {
    char str[80];
    char subStr[80];
    int result;

    printf("請輸入源字符串:");
    gets(str);
    printf("請輸入子字符串:");
    gets(subStr);

    result = KMPSearch(str, subStr);
    if (result == -1) {
        printf("字符串[%s]不存在子串[%s]\n", str, subStr);
    } else {
        printf("子串[%s]第一次出現在字符串[%s]中的下標為%d\n", subStr, str, result);
    }

    return 0;
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM