Boyer-Moore 精確匹配演算法實現(C/C++)

牛晨光發表於2012-03-09

演算法原理這裡不廢話,網上找到的演算法很多再臨界情況的處理都有錯誤,所以自己重寫了一個。

預處理建立shift資源表時的效率比較一般,但是簡單易懂! 如果你有更好的版本希望發來研究研究。


/*
 * Boyer-Moore 精確匹配演算法
 * -------------------------------------------
 * copyright (c) 2012 Niu Chenguang <chrisniu1984@gmail.com>
 *
 * file: bm.h
 */

#ifndef __BM_H__
#define __BM_H__

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

#define BM_SKIP_SIZE    256
#define BM_SHIFT_SIZE   128 // 此值決定字串最大長度

/*
 * 為已知字串建立skip資源表。
 *
 * @skip: [in/out] 儲存skip資源的int陣列。元素數必須為BM_SIKP_SIZE個。
 * @sub: [in] 已知字串
 * @sub_len: [in] 字串長度
 *
 * @RETURN: 0 = 成功,-1 = 失敗
 */
static inline int bm_make_skip(int skip[], unsigned char *sub, int sub_len)
{
    if (NULL == skip || NULL == sub || sub_len <= 0) {
        return -1;
    }

    memset(skip, 0x00, sizeof(int)*BM_SKIP_SIZE);

    int i;
    for (i=0; i<BM_SKIP_SIZE; i++) {
        skip[i] = sub_len;
    }

    while (sub_len != 0) {
        skip[*sub] = --sub_len;
        sub++;
    }

    return 0;
}

/*
 * 為已知字串建立shift資源表。
 *
 * @shift: [in/out] 儲存shift資源的int陣列。元素數必須為BM_SHIFT_SIZE個。
 * @sub: [in] 已知字串
 * @sub_len: [in] 字串長度
 *
 * @RETURN: 0 = 成功,-1 = 失敗
 */
static inline int bm_make_shift(int shift[], unsigned char* sub,int sub_len)
{
    if (NULL == shift || NULL == sub ||
        sub_len <= 0 || sub_len > BM_SHIFT_SIZE) {
        return -1;
    }
    memset(shift, 0x00, sizeof(int)*BM_SHIFT_SIZE);

    int pos;
    for (pos = sub_len-1; pos >= 0; pos--) {
        //int i=0;
        //printf("---------------------\n");
        //printf("%s\n", sub);
        //for (i=0; i<pos; i++) {
        //    printf(" ");
        //}
        //printf("%s\n", sub+pos);

        unsigned char *good = sub + pos + 1; // 好字尾起始位置。
        int good_len = sub_len - pos - 1;  // 好字尾長度

        // 此迴圈是為了匹配到儘量長的好字尾串
        while (good_len > 0) {

            // p 為開始位置查詢好字尾
            unsigned char *p = sub + sub_len - 1 - good_len;

            // 此迴圈是為了從右向左逐個位置查詢好字尾串。
            while (p >= sub) {
                // 在p位置找到了good
                if (memcmp(p, good, good_len) == 0) {
                    shift[pos] = (sub_len-pos)+(good-p)-1;
                    break;
                }

                // 向左移動一個位置,準備繼續查詢good串
                p--;
            }

            // 此shift位置有值,說明查詢成功,直接break
            if (shift[pos] != 0) {
                break;
            }

            // 取好字尾的子串
            good++;
            good_len--;
        }

        // 此shift位置沒有值,說明沒有匹配到good串及其字串
        if (shift[pos] == 0) {
            shift[pos] = sub_len-pos;
        }

        //for(i=0;i<shift[pos]-(sub_len-pos);i++){
        //    printf(" ");
        //}
        //printf("%s\n", sub);
    }

    return 0;
}

static inline int bm_search(unsigned char *str, int str_len,
                         unsigned char *sub, int sub_len,
                         int skip[], int shift[])
{
    if (sub_len == 0) {
        return 1;   // 根據實際需要,修改這個返回值。
    }

    if (sub_len > str_len) {
        return 0;
    }

    int str_end = sub_len - 1;
    while (str_end <= str_len) {
        int sub_end = sub_len-1;

        while (str[str_end] == sub[sub_end]) {
            if (sub_end == 0) {
                return 1;
            }
            str_end--;
            sub_end--;
        }

        int skip_stride = skip[str[str_end]];
        int shift_stride = shift[sub_end];

        str_end += MAX(skip_stride,shift_stride);
    }

    return 0;
}

#endif // __BM_H__


相關文章