簡單模式匹配算法和KMP算法

參見原文
簡單模式匹配算法
經典的模式匹配,是讓模式串的首字符從主串的首字符開始向右匹配。

匹配的規則是:

1.在主串的第i個位置,讓pos=i,如果主串pos位置的字符等於模式串的第一個字符,則pos++,再與模式串的第二個字符進行匹配。
2.如果又相等則繼續向下匹配,直到匹配到模式串的結尾,就說明主串中存在該模式串;如果不相等,則停止匹配,回到主串中,並i++。
3.然後再讓pos=i,重複上述步驟,直到i到達主串結尾。
相應代碼如下:

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

int main() {
    char str[10000],pat[10000];
    printf("please push the main string:\n");
    scanf("%s",str);
    printf("please push the pattern:\n");
    scanf("%s",pat);
    int lenS=strlen(str),lenP=strlen(pat);
    //從主串第一位置開始向後匹配,直到結尾
    for(int i=0;i<lenS;i++)
    {
        int pos=i,j=0;
        while(j<lenP)
        {
            //如果主串pos位置與模式串的j位置相等,則都向後一位
            if(pat[j]==str[pos])
            {
                j++;pos++;
            }
            //否則中斷與模式串的匹配
            else break;
        }
        //判斷與模式串是否完全匹配,若是則輸出i位置
        if(pos-i==lenP) printf("\npattern start in %d",i);
    }
}

KMP模式匹配算法
KMP算法的好處就是不用回溯,而是以一種滑動的方式向下匹配模式串。

假設next數組
首先,我們假設對於模式串有一個next數組。

該數組值的含義是:模式串第0位後長度爲next[j]的字符串,與第j位(從0開始,除0外)前長度爲next[j]的字符串相等,同時next[j]值要竟可能大,但兩個字符串又不能重合。特例:第0位的next值爲-1。

例如:abcac模式串的next數組值依次爲[-1,0,0,0,1]。模式串中第4位(從0開始)字符c的next值爲1,表明字符c前長度爲1的字符串即a,與從0位開始長度爲1的字符串即a相等。其餘位,除0位外,next值都爲0。

再例如:0001模式串的next數組值依次爲[-1,0,1,2]。

利用next數組進行模式匹配
在普通模式匹配中,主串pos位與模式串j位不匹配時,主串回到i+1位重新與模式串0位進行匹配。

有了next數組,我們便不用回溯到i+1位重新匹配了。而是用主串pos位與模式串next[j]位進行匹配,若還是不匹配,則轉到next[next[j]]。直到next的值等於-1,模式串滑動到主串pos+1位重新匹配。

其實,當不匹配求next[j]值時,就是將模式串向下滑動的過程。其原理是因爲next數組的特殊性:
模式串第0位後長度爲next[j]的字符串,與第j位(從0開始,除0外)前長度爲next[j]的字符串相等。

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

// 獲取模式串的next數組函數:
// 將模式串既當作“主串”又當作模式串,同樣按照KMP的算法進行匹配求next值。
void getNext(char *str,int *next,int n) {

    next[0]=-1;
    int i=1;
    int j=-1;
    while (i<n) {
        // 如果j==-1,則“主串”i位置的next值爲0。
        // 前進到“主串”的下一位,並使模式串從頭開始匹配
        if (j == -1) {
            next[i]=0;
            i++;
            j=0;
        }
        // 如果“主串”i位置的前一位和模式串j位置相同,則“主串”i位置的next值爲j+1。
        // 同時“主串”與模式串繼續向下匹配。
        else if (str[i-1] == str[j]) {
            next[i]=j+1;
            i++;
            j++;
        }
        // 如果不匹配,則前進到模式串的next[j]位置
        else j=next[j];
    }
}

int main() {

    char str[10000],pat[10000];
    printf("please push the main string:\n");
    scanf("%s",str);
    printf("please push the pattern:\n");
    scanf("%s",pat);
    int lenS=strlen(str),lenP=strlen(pat);

    // next數組:當模式串第j位與主串當前第i位不匹配時,將模式串向後滑動,
    //          用模式串中第next[j]位再與主串中的第i位比較。
    //          若next[j]==-1,則將模式串整體向後移動一位,與主串中第i+1位繼續比較。
    int *next=(int*)malloc(sizeof(int)*lenP);
    getNext(pat, next, lenP);

    int i=0,j=0;    // i爲主串中字符位置,j爲模式串中字符位置
    while (i<lenS) {
        // 如果j==-1,則模式串整體向後移動一位,與主串中第i+1位比較
        if (j == -1) {
            i++;
            j=0;
        }
        // 如果主串i位置與模式串j位置相等,則i、j都向下繼續配對
        else if (str[i] == pat[j]) {
            i++;
            j++;
            if (j == lenP) {
                printf("find pattern in %d\n", i-lenP);
                j=0;
            }
        }
        // 如果模式串j位置與主串i位置不匹配,則將模式串向後滑動,
        // 用模式串的next[j]位置與主串i位置匹配
        else j=next[j];
    }
    free(next);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章