字符串匹配之BM算法

/* 使用Boyer-Moore-Horspool-Sunday 算法进行字符串匹配的系列函数
算法提出:BOYER, R., and S. MOORE. 1977. "A Fast String Searching Algorithm."
                         HORSPOOL, R. N. 1980. "Practical Fast Searching in Strings."
                          Software - Practice and Experience, 10, 501-06. Further improvements by HUME, A., and D. M. SUNDAY. 1991.
   2002.08.20 周霖 KCN
*/
#include "system.h"
/* 字符串查找函数*/
char *bm_strstr(const char *string, const char *pattern)
{
    size_t shift[256];
    bool init = false;

    return (char *) memfind(string, strlen(string), pattern, strlen(pattern), shift, &init);
}

/* 字符串多次匹配函数*/
char *bm_strstr_rp(const char *string, const char *pattern, size_t * shift, bool * init)
{
    return (char *) memfind(string, strlen(string), pattern, strlen(pattern), shift, init);
}

/* 字符串大小写不敏感的匹配函数*/
char *bm_strcasestr(const char *string, const char *pattern)
{
    size_t shift[256];
    bool init = false;

    return (char *) txtfind(string, strlen(string), pattern, strlen(pattern), shift, &init);
}

/* 字符串多次大小写不敏感匹配函数*/
char *bm_strcasestr_rp(const char *string, const char *pattern, size_t * shift, bool * init)
{
    return (char *) txtfind(string, strlen(string), pattern, strlen(pattern), shift, init);
}

/* 内存匹配函数memfind
*/
void *memfind(const void *in_block,     /* 数据块 */
              const size_t block_size,  /* 数据块长度 */
              const void *in_pattern,   /* 需要查找的数据 */
              const size_t pattern_size,        /* 查找数据的长度 */
              size_t * shift,   /* 移位表,应该是256*size_t的数组 */
              bool * init)
{                               /* 是否需要初始化移位表 */
    size_t byte_nbr,            /* Distance through block */
     match_size,                /* Size of matched part */
     limit;
    const unsigned char *match_ptr = NULL;
    const unsigned char *block = (unsigned char *) in_block,    /* Concrete pointer to block data */
        *pattern = (unsigned char *) in_pattern;        /* Concrete pointer to search value */

    if (block == NULL || pattern == NULL || shift == NULL)
        return (NULL);

/* 查找的串长应该小于 数据长度*/
    if (block_size < pattern_size)
        return (NULL);

    if (pattern_size == 0)      /* 空串匹配第一个 */
        return ((void *) block);

/* 如果没有初始化,构造移位表*/
    if (!init || !*init) {
        for (byte_nbr = 0; byte_nbr < 256; byte_nbr++)
            shift[byte_nbr] = pattern_size + 1;
        for (byte_nbr = 0; byte_nbr < pattern_size; byte_nbr++)
            shift[(unsigned char) pattern[byte_nbr]] = pattern_size - byte_nbr;

        if (init)
            *init = true;
    }

/*开始搜索数据块,每次前进移位表中的数量*/
    limit = block_size - pattern_size + 1;
    for (byte_nbr = 0; byte_nbr < limit; byte_nbr += shift[block[byte_nbr + pattern_size]]) {
        if (block[byte_nbr] == *pattern) {
            /*
             * 如果第一个字节匹配,那么继续匹配剩下的
             */
            match_ptr = block + byte_nbr + 1;
            match_size = 1;

            do {
                if (match_size == pattern_size)
                    return (void *) (block + byte_nbr);
            } while (*match_ptr++ == pattern[match_size++]);
        }
    }
    return NULL;
}

/* 大小写不敏感的匹配函数txtfind
*/
void *txtfind(const void *in_block,     /* 数据块 */
              const size_t block_size,  /* 数据块长度 */
              const void *in_pattern,   /* 需要查找的数据 */
              const size_t pattern_size,        /* 查找数据的长度 */
              size_t * shift,   /* 移位表,应该是256*size_t的数组 */
              bool * init)
{                               /* 是否需要初始化移位表 */
    size_t byte_nbr,            /* Distance through block */
     match_size,                /* Size of matched part */
     limit;
    const unsigned char *match_ptr = NULL;
    const unsigned char *block = (unsigned char *) in_block,    /* Concrete pointer to block data */
        *pattern = (unsigned char *) in_pattern;        /* Concrete pointer to search value */

    if (block == NULL || pattern == NULL || shift == NULL)
        return (NULL);

/* 查找的串长应该小于 数据长度*/
    if (block_size < pattern_size)
        return (NULL);

    if (pattern_size == 0)      /* 空串匹配第一个 */
        return ((void *) block);

/* 如果没有初始化,构造移位表*/
    if (!init || !*init) {
        for (byte_nbr = 0; byte_nbr < 256; byte_nbr++)
            shift[byte_nbr] = pattern_size + 1;
        for (byte_nbr = 0; byte_nbr < pattern_size; byte_nbr++)
            shift[(unsigned char) tolower(pattern[byte_nbr])] = pattern_size - byte_nbr;

        if (init)
            *init = true;
    }

/*开始搜索数据块,每次前进移位表中的数量*/
    limit = block_size - pattern_size + 1;
    for (byte_nbr = 0; byte_nbr < limit; byte_nbr += shift[tolower(block[byte_nbr + pattern_size])]) {
        if (tolower(block[byte_nbr]) == tolower(*pattern)) {
            /*
             * 如果第一个字节匹配,那么继续匹配剩下的
             */
            match_ptr = block + byte_nbr + 1;
            match_size = 1;

            do {
                if (match_size == pattern_size)
                    return (void *) (block + byte_nbr);
            } while (tolower(*match_ptr++) == tolower(pattern[match_size++]));
        }
    }
    return NULL;

<think>好的,我需要回答用户关于BM字符串匹配算法的实现和应用的问题。首先,我得回忆一下BM算法的基本概念和原理。BM算法由Boyer和Moore提出,核心思想是通过预处理模式串,利用坏字符规则和好后缀规则,在匹配失败时跳过尽可能多的字符,从而提高匹配效率。用户提供的引用中提到BM算法比KMP高效,是常用算法之一,所以我要强调这两点。 接下来,实现部分需要详细说明坏字符和好后缀规则的具体步骤。坏字符规则是指当主串和模式串不匹配时,找到模式串中该坏字符最后出现的位置,然后移动模式串使对齐。这里需要注意如果坏字符不在模式串中,则直接移动整个模式串长度。好后缀规则则是查找已经匹配的后缀在模式串中其他位置的出现情况,决定移动的距离。这两个规则需要预处理生成两个数组:坏字符表和好后缀表。 然后,我需要给出一个具体的实现步骤,包括如何构建这两个表,以及如何在实际匹配中使用它们。可能还需要提到如何选择坏字符和好后缀规则中的最大移动值,以确保跳过最多的字符,提高效率。 关于应用部分,用户提供的引用中提到BM算法在数据库查询、文本编辑器等场景的应用,需要结合实际例子说明其高效性。同时,可以对比暴力匹配和KMP算法,突出BM的优势,比如预处理带来的加速效果。 还要注意用户提到的格式要求,数学公式用$...$,独立公式用$$...$$,并正确使用LaTeX语法。例如,坏字符规则的移动距离计算可能需要公式表示。此外,需要生成相关问题,这些问题应围绕BM算法的优化、与其他算法的比较以及具体应用场景展开。 最后,确保回答结构清晰,分步骤解释实现和应用,引用用户提供的参考资料,并在适当位置添加引用标识。检查是否有遗漏的关键点,比如预处理的时间复杂度,或者实际代码示例,但根据用户提供的引用,可能不需要代码,但需要详细说明步骤。确保语言口语化,避免技术术语堆砌,让用户容易理解。</think>### BM字符串匹配算法实现与应用详解 BM(Boyer-Moore)算法是一种高效的字符串匹配算法,其核心思想是通过**坏字符规则**和**好后缀规则**,在模式串与主串不匹配时跳过尽可能多的无效比较,从而将时间复杂度优化至$O(n/m)$(最坏情况下为$O(n \cdot m)$)[^1]。以下是其实现细节和应用场景分析: --- #### 一、BM算法核心规则 1. **坏字符规则(Bad Character Rule)** - **触发条件**:主串中某个字符与模式串不匹配。 - **移动逻辑**: - 若坏字符在模式串中存在,则将模式串**右移**使模式串中**最后出现的该字符**与主串坏字符对齐。 - 若坏字符不在模式串中,则直接右移整个模式串长度。 - 移动距离公式:$$d_1 = \max(1, k - \text{坏字符在模式串中最后出现的位置})$$ 其中$k$为坏字符在模式串中的当前索引。 2. **好后缀规则(Good Suffix Rule)** - **触发条件**:已匹配到长度为$m$的后缀子串。 - **移动逻辑**: - 查找模式串中是否存在与好后缀匹配的其他子串,若有则对齐该子串。 - 若不存在完全匹配的子串,则查找模式串前缀中是否与好后缀的后缀匹配。 - 移动距离公式:$$d_2 = \text{模式串长度} - \text{好后缀在模式串中最后一次出现的位置}$$ 3. **最终移动距离**:选择$d_1$和$d_2$中的较大值,确保跳过最多无效比较。 --- #### 二、BM算法实现步骤 1. **预处理阶段** - **构建坏字符表**:记录每个字符在模式串中最后一次出现的位置。 ```python def build_bad_char_table(pattern): table = {} for i, c in enumerate(pattern): table[c] = i # 覆盖重复字符,保留最后出现的位置 return table ``` - **构建好后缀表**:计算每个后缀子串在模式串中的匹配位置。 - 使用后缀数组和前缀匹配规则生成。 2. **匹配阶段** - 从主串与模式串的**右端开始比对**。 - 若发现不匹配字符,分别计算两种规则的移动距离,选择较大值移动模式串。 - 重复直到找到匹配或遍历完主串。 --- #### 三、BM算法应用场景 1. **大规模文本搜索**:如数据库的`LIKE`查询、日志分析工具,利用跳跃式匹配减少比较次数[^2]。 2. **代码编辑器中的查找功能**:快速定位长代码文件中的关键字。 3. **生物信息学**:DNA序列匹配中处理长模式串的高效需求。 4. **网络安全**:恶意特征码扫描时的高吞吐量匹配。 --- #### 四、BM算法性能优势 - **跳跃能力**:通过坏字符和好后缀规则,平均跳过$O(n/m)$次比较,性能优于暴力匹配($O(n \cdot m)$)和KMP算法($O(n + m)$)[^1]。 - **空间复杂度**:预处理需要$O(m + \Sigma)$空间($\Sigma$为字符集大小),适用于模式串较短场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值