資料結構與演算法 - 串

终末时代人發表於2024-11-03

KMP字串匹配演算法

image
next陣列的計算方法:
看該字元前的字串的字首和字尾有多少相同(可以交叉重疊),就讓相同的數量值加一即為當前next值。
也可以這樣計算:看前一個字元的next值處是否與前一個字元相同,若相同,則當前next值為上一next值加一;若不相同,則檢視上一next值處的字元的next處於當前的字元是否相同,直到相同之後,讓相同的值加一(如果到達最開頭都不相同,則直接設定為1,總之就是到相同的位置的next值加一)。
下面是一個例子:
image

A B C A B C D
0 1 1 1 2 3 4

因此,在第一次失配時,如上圖紅框區域,j指標直接回到了next[j]的位置,當j位於第一個時(即第一個就失配),i++並且j不變,以此類推
nextval陣列的計算方法
規則:
對比當前next值處的字元是否與當前字元相同,若相同,則nextval也與那字元的nextval相同;若不同,則nextval與其next值相同;
例子:

索引 1 2 3 4 5 6 7 8 9 10 11 12
字串 a b a b a a a b a b a a
next陣列 0 1 1 2 3 4 2 2 3 4 5 6
nextval陣列 0 1 0 1 0 4 2 1 0 1 0 4
解釋: 前一個是b,b和a不相同,到第一個,設定為1 前一個是a,與b不一樣,和b還是不一樣,和a一樣,1+1=2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
using namespace std;

#define MAXSTRLEN 255
typedef unsigned char SString[MAXSTRLEN + 1];

void strLength(SString S) {
    int m;
    for (m = 1; S[m] != '\0'; m++);
    S[0] = m - 1;
}

void get_next(SString T, int* next) {
    int j = 1, k = 0;
    next[1] = 0; // 初始化 next[1] 為 0

    while (j < T[0]) {  // T[0] 是串 T 的長度
        if (k == 0 || T[j] == T[k]) {
            j++;
            k++;
            next[j] = k;
        }
        else {
            k = next[k];
        }
    }
}

int Index_KMP(SString S, SString T, int pos, int* next) {
    int i = pos, j = 1; // i 指向主串 S 的當前字元,j 指向模式串 T 的當前字元
    get_next(T, next);  // 計算模式串 T 的 next 陣列

    while (i <= S[0] && j <= T[0]) {
        if (j == 0 || S[i] == T[j]) {  // 如果匹配,或 j = 0(即從頭匹配)
            i++;
            j++;
        }
        else {
            j = next[j];  // 如果失配,j 跳轉到 next[j]
        }
    }

    if (j > T[0]) return i - T[0]; // 匹配成功
    else return 0;  // 匹配不成功
}

int main() {
    SString S, T;
    int pos;
    int next[MAXSTRLEN];
    int r;

    printf("輸入主串 S: ");
    scanf("%s", S + 1);  // 跳過下標為 0 的元素
    printf("輸入模式串 T: ");
    scanf("%s", T + 1);  // 跳過下標為 0 的元素
    printf("輸入起始位置 pos: ");
    scanf("%d", &pos);

    strLength(S);   // 求主串 S 的長度
    strLength(T);   // 求模式 T 的長度

    if (r = Index_KMP(S, T, pos, next))
        printf("模式串在主串中的位置為:  %d \n", r);
    else printf("匹配失敗!");

    return 0;
}

相關文章