1. 程式人生 > >串匹配問題-BF演算法、KMP演算法、BM演算法

串匹配問題-BF演算法、KMP演算法、BM演算法

BF演算法

int BF(char A[], char B[]){
    int i = 0, j = 0;
    while(A[i] != '\0' && B[j] != '\0'){
        if(A[i] == B[j]){
            i++;
            j++;
        }else{
            i = i - j + 1;
            j = 0;
        }
    }

    if(B[j] == '\0'){
        return i - j + 1;
    }else{
        return -1;
    }
}

KMP演算法:

int KMP(char A[], char B[]){
    int i = 0, j = 0;
    int next[80];
    GetNext1(B, next);
    while(A[i] != '\0' && B[j] != '\0'){
        if(A[i] == B[j]){
            i++;
            j++;
        }else{
            j = next[j];
            if(j == -1){
                i++;
                j++;
            }

        }
    }
    if(B[j] != '\0'){
        return -1;
    }else{
        return (i - j + 1);
    }


}

KMP演算法獲取next[]值得兩種方法:

    方法一:

void GetNext(char B[], int next[]){
    int i , j, len;
    next[0] = -1;
    for(i = 1; B[i] != '\0'; i++){ //依次求next[i]
        for(len = i - 1; len > 0; len--){//len:相等時的字首的最大長度為i-1,不滿足相等時就依次減小 直到找到最大長度 即為next【j】
            for(j = 0; j < len; j++){ //比較B的字首和字尾是否相等
                if(B[j] != B[i - len + j])
                    break;
            }
            if(j == len){//上步for迴圈跳出後,表示j走到了最大相等字首的下一位,也等於字首的長度
                next[i] = len ;
                break;
            }
        }
        if(len < 1){//字首的最大長度為0 時
            next[i] = 0;
        }
    }


}

      方法二:

void GetNext1( char B[], int next[]){
    int j = 0, k = -1;
    next[0] = -1;

    while(B[j] != '\0'){
        if(k == -1){ //無相等子串
            j++;
            next[j] = 0;
        }else if(B[j] == B[k]){
            next[++j] = k + 1;

        }else{
            k = next[k];

        }
    }
}

BM演算法:

int BMSearch(char *buf, int blen, char *ptrn, int plen, int *skip, int *shift)
{
    int b_idx = plen;
    if (plen == 0)
        return 1;
    while (b_idx <= blen)//計算字串是否匹配到了盡頭
    {
        int p_idx = plen, skip_stride, shift_stride;
        while (buf[--b_idx] == ptrn[--p_idx])//開始匹配
        {
            if (b_idx < 0)
                return 0;
            if (p_idx == 0)
            {
                return b_idx;  //匹配到
            }
        }
        skip_stride = skip[(unsigned char)buf[b_idx]];//根據壞字元規則計算跳躍的距離
        shift_stride = shift[p_idx];//根據好字尾規則計算跳躍的距離
        b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;//取大者
    }
    return 0;
}

BM演算法獲取壞字元表:

int* MakeSkip(char *btrn, int bLen)
{
    int i;
    //為建立壞字元表,申請256個int的空間
    //PS:之所以要申請256個,是因為一個字元是8位,
    // 所以字元可能有2的8次方即256種不同情況
    int *skip = (int*)malloc(256*sizeof(int)); //頭指標

    if(skip == NULL)
    {
        printf("Error");
        return 0;
    }

    //初始化壞字元表,256個單元全部初始化為pLen
    for(i = 0; i < 256; i++)
    {
        *(skip+i) = bLen;
    }

    //給表中需要賦值的單元賦值,不在模式串中出現的字元就不用再賦值了
   //賦值,從左到右遍歷btrn,這樣如果一個字元出現兩次,後面的覆蓋前面的,
    //不在模式中出現的字元不用再賦值,它們使用預設值bLen。
    while(bLen != 0)
    {
        *(skip+(int)*btrn++) = bLen--;
    }

    return skip;
}

BM演算法獲取好字尾表:

int* MakeShift(char* btrn,int bLen)
{
    //為好字尾表申請pLen個int的空間
    //這樣,第一個位置放置長度為1的字尾
    int *shift = (int*)malloc(bLen*sizeof(int));
    int *sptr = shift + bLen - 1;//方便給好字尾表進行賦值的指標
    char *pptr = btrn + bLen - 1;//記錄好字尾表邊界位置的指標
    char c;

    if(shift == NULL)
    {
        printf("Error");
        return 0;
    }

    c = *(btrn + bLen - 1);//儲存模式串中最後一個字元,因為要反覆用到它

    *sptr = 1;//以最後一個字元為邊界時,確定移動1的距離(因為要與壞字元規則比較,所以這個是個假設,1也是最小的移動距離)

    pptr--;//邊界移動到倒數第二個字元

    while(sptr-- != shift)//該最外層迴圈完成給好字尾表中每一個單元進行賦值的工作
    {
        char *p1 = btrn + bLen - 2, *p2,*p3;

        //該do...while迴圈完成以當前pptr所指的字元為邊界時,要移動的距離
        do{
            while(p1 >= btrn && *p1-- != c);//該空迴圈,尋找與最後一個字元c匹配的字元所指向的位置

            p2 = btrn + bLen - 2;
            p3 = p1;

            while(p3 >= btrn && *p3-- == *p2-- && p2 >= pptr);//該空迴圈,判斷在邊界內字元匹配到了什麼位置

        }while(p3 >= btrn && p2 >= pptr);

        *sptr = shift + bLen - sptr + p2 - p3;//儲存好字尾表中,以pptr所在字元為邊界時,要移動的位置

        pptr--;//邊界繼續向前移動
    }

    return shift;
}

主函式:

int main()
{

    char A[] = "abcdesdeacd";
    char B[] = "acd";
    int k = BF(A, B);
    printf("%d\n", k);

    k = KMP(A, B);
     printf("%d\n", k);

    char *T = "abcdesdeacd";
    char *P = "acd";
    int *skip = NULL;
    int *shift = NULL;
    skip = MakeSkip(P, strlen(P));
    shift = MakeShift(P, strlen(P));


    printf("%d\n", BMSearch(T, strlen(T), P, strlen(P), skip, shift) + 1);
    return 0;
}

測試結果: