**朴素字符串匹配算法(Brute-Force,简称BF算法)**的核心实现,其基本思想是暴力枚举主串中每一个可能的起始位置

**朴素字符串匹配算法(Brute-Force,简称BF算法)**的核心实现,其基本思想是暴力枚举主串中每一个可能的起始位置,逐字符与模式串进行匹配。

1. 算法核心思想

从主串的第一个字符开始,将模式串逐位与主串对齐比较:

  • 如果当前字符匹配,则继续比较后续字符;
  • 一旦出现不匹配,则主串指针回退到本次匹配的起始位置的下一个位置,模式串指针重置为0,重新开始匹配;
  • 若模式串全部字符都成功匹配,则返回在主串中的起始下标;
  • 若遍历完主串仍未找到完全匹配的位置,则返回 -1。

2. 代码实现(C语言)

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

// 朴素字符串匹配算法
int bfSearch(char* s, char* t) {
    int i = 0;  // 主串指针
    int j = 0;  // 模式串指针
    int slen = strlen(s);
    int tlen = strlen(t);

    while (i < slen && j < tlen) {
        if (s[i] == t[j]) {
            i++;
            j++;
        } else {
            i = i - j + 1;  // 主串回退
            j = 0;          // 模式串重置
        }
    }

    // 匹配成功时,j等于tlen
    if (j == tlen) {
        return i - tlen;  // 返回起始位置
    } else {
        return -1;  // 匹配失败
    }
}

// 示例测试
int main() {
    char s[] = "ABABCABABA";
    char t[] = "ABABA";
    int index = bfSearch(s, t);
    if (index != -1)
        printf("匹配成功,起始下标为:%d\n", index);
    else
        printf("匹配失败\n");
    return 0;
}

3. 时间复杂度分析

  • 最好情况:每次匹配第一个字符就失败,或者很快失败,平均每个位置只需常数次比较。例如主串和模式串首字符差异大,时间复杂度接近 O(n + m)
  • 最坏情况:主串形如 "AAAAAAAAB",模式串为 "AAAB",每次都要比较到末尾才失败,每趟比较 m 次,共 n - m + 1 趟,总时间复杂度为 O((n - m + 1) * m),即 O(n × m)

优点:逻辑清晰、易于理解与实现。
缺点:效率低,尤其在重复字符多的情况下性能差。
🔧 改进方向:使用 KMP、Boyer-Moore、Sunday 等更高效的字符串匹配算法优化最坏情况下的表现。

KMP(Knuth-Morris-Pratt)算法通过预处理模式串构建“部分匹配表”(即next数组),利用模式串自身的重复信息,在发生不匹配时不回退主串指针,而是将模式串尽可能向右滑动到一个合理位置,继续比较,从而避免重复匹配,提升效率。

核心思想:利用模式串的“最长公共前后缀”进行跳跃

当主串 s[i] 与模式串 t[j] 不匹配时:

  • 朴素算法会将主串指针 i 回退到起始位置的下一个位置,重新从 j=0 开始匹配;
  • KMP算法则认为:如果模式串前缀中存在重复结构(如 "ABAB""AB" 是前后缀),就可以利用这个结构,把模式串“滑动”到下一个可能匹配的位置,而主串指针 i 不需要回退,只需调整模式串指针 jnext[j-1] 的位置继续比较。

1. next数组定义

next[j] 表示:在模式串中,以 j 结尾的子串的最长相等真前后缀长度(真前后缀指不包括整个字符串本身)。
换句话说,next[j] 告诉我们:当 t[j] 匹配失败时,j 应该跳转到哪个位置继续匹配。

示例:

模式串:"ABABA"

j     : 0 1 2 3 4
t[j]  : A B A B A
next[j]:0 0 1 2 3

解释:

  • j=0: 单字符无前后缀 → next[0]=0
  • j=1: “AB” 无公共前后缀 → next[1]=0
  • j=2: “ABA” 最长公共前后缀是 “A”(长度1)→ next[2]=1
  • j=3: “ABAB” 公共前后缀 “AB”(长度2)→ next[3]=2
  • j=4: “ABABA” 公共前后缀 “ABA”(长度3)→ next[4]=3

2. 匹配过程(不回退主串指针)

int kmpSearch(char* s, char* t) {
    int i = 0, j = 0;
    int slen = strlen(s), tlen = strlen(t);
    int* next = buildNext(t); // 构建next数组

    while (i < slen) {
        if (j == -1 || s[i] == t[j]) {
            i++;
            j++;
        } else {
            j = next[j];  // 模式串指针跳转,主串i不回退
        }
        if (j == tlen) {
            return i - tlen;  // 找到匹配
        }
    }
    return -1;
}

关键点:只有 j = next[j],而 i 一直向前走,没有回退!


3. 时间复杂度分析

  • 构建next数组:O(m)
  • 匹配过程:O(n)
  • 总时间复杂度:O(n + m)

相比朴素算法最坏 O(n×m),KMP 在主串很长且模式串有重复结构时优势明显。


总结

KMP 算法之所以能避免主串指针回退,是因为它利用了模式串内部的自相似性(重复前缀),通过 next 数组指导模式串的滑动位置,使得每次失配后都能跳过不可能成功的匹配位置,主串只需遍历一次即可完成匹配。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值