KMP算法

BF算法

BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将主串SS的第一个字符与模式串TT的第一个字符进行匹配,若相等,则继续比较SS的第二个字符和TT的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种暴力算法。
我们来看一个例子
在这里插入图片描述
BF算法我们很明显可以看到会出现很多多余的匹配,因为匹配错误后就要回退到开始的下一个位置,这就导致我们BF算法的时间复杂度为O(m*n)
接下来我们讲解一下BF算法优化后的算法KMP算法

KMP算法

KMP算法的原理:

1.在匹配的过程中,目标串的指针不需要不回溯,只回溯模式串的指针;
2.如果模式串和目标串前n个字符匹配成功,遇到匹配失败的字符时,模式串的指针回溯的位置由模式串的内容决定(回溯到 匹配失败位置前的模式串内容的最长公共前后缀 的长度+1),然后继续比较。

我们先来演示以下KMP算法
在这里插入图片描述
在这里插入图片描述
我们还需要讲解一个概念叫公共前后缀,最长公共前后缀
在这里插入图片描述
1.根据模式串的内容,生成一个遇到匹配失败的字符时模式串的指针回溯的位置的数组(next
数组)。
2.在BF算法上稍作修改,当匹配失败时,模式串的位置指针回溯next数组指示的位置。
在这里插入图片描述

求next数组代码如下

#include <iostream>
#include <vector>
#include <string>

using namespace std;

// 函数:计算模式串的Next数组
void getNext(const string& p, vector<int>& ne) {
    int m = p.size();
    ne.resize(m + 1); // 创建Next数组,大小为m + 1
    ne[0] = 0; // ne[0]通常设为0,表示空串
    int j = 0; // j是当前匹配的前缀末尾

    // 从第2个字符开始计算Next数组
    for (int i = 2; i <= m; i++) {
        // 如果不匹配,回退j到上一个前缀
        while (j > 0 && p[i - 1] != p[j]) {
            j = ne[j]; // 回退到ne数组
        }
        // 如果匹配,j加1
        if (p[i - 1] == p[j]) {
            j++;
        }
        // 设置Next数组的值
        ne[i] = j; // 更新ne数组
    }

    // 输出Next数组
    cout << "Next数组为: ";
    for (int i = 1; i <= m; i++) {
        cout << ne[i] << " "; // 从1开始输出Next数组
    }
    cout << endl;
}

int main() {
    string p = "ababc"; // 模式串
    vector<int> ne; // 存储Next数组

    getNext(p, ne); // 计算并输出Next数组

    return 0;
}

代码所输出的值为0 0 1 2 0和我们画图所得的0 1 1 2 3不同,其实我们只需要把0 0 1 2 0向右移动填上-1就为-1 0 0 1 2,当时我们说过当下标为0时next数组要-1,那么0 1 1 2 3就会变为-1 0 0 1 2,此时就一样了,我们代码所求的是最长公共前缀和,其实只是不同类型的next表。

KMP算法代码如下:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

 //函数:计算模式串的Next数组
void getNext(const string& p, vector<int>& ne) {
    int m = p.size();
    ne.resize(m + 1);
    ne[0] = 0;
    int j = 0;

    for (int i = 2; i <= m; i++) {
        while (j > 0 && p[i - 1] != p[j]) {
            j = ne[j];
        }
        if (p[i - 1] == p[j]) {
            j++;
        }
        ne[i] = j;
    }
}

// 函数:KMP字符串匹配
void kmpMatch(const string& s, const string& p) {
    int n = s.size(); // s的长度
    int m = p.size(); // p的长度
    vector<int> ne; // 存储Next数组
    getNext(p, ne); // 计算Next数组

    int j = 0; // j是模式串的指针
    // 遍历文本串
    for (int i = 1; i <= n; i++) {
        // 当不匹配时,根据Next数组回退j的值
        while (j > 0 && s[i - 1] != p[j]) {
            j = ne[j];
        }
        // 当字符匹配时,j加1
        if (s[i - 1] == p[j]) {
            j++;
        }
        // 如果j等于模式串的长度,表示找到匹配
        if (j == m) {
            cout << "匹配成功,起始位置:" << i - j << endl; // 输出匹配成功的起始位置
            j = ne[j]; // 根据Next数组继续查找下一个匹配
        }
    }
}

int main() {
    string s = "aabacabababcaaab"; // 长文本
    string p = "ababc"; // 模式串

    kmpMatch(s, p); // 调用KMP匹配函数

    return 0;
}

为了避免出现以下有多余匹配的情况我们可以把next数组优化成nextval数组(知道如何优化即可)
在这里插入图片描述
KMP匹配和next数组源码

### KMP算法的实现 KMP算法是一种高效的字符串匹配算法,它通过构建部分匹配表(也称为`next`数组)来减少不必要的回溯操作[^2]。以下是基于Python语言的KMP算法实现: ```python def compute_next_array(pattern): next_arr = [-1] * len(pattern) i, j = 0, -1 while i < len(pattern) - 1: if j == -1 or pattern[i] == pattern[j]: i += 1 j += 1 next_arr[i] = j else: j = next_arr[j] return next_arr def kmp_search(text, pattern): m, n = len(text), len(pattern) next_arr = compute_next_array(pattern) i, j = 0, 0 while i < m and j < n: if j == -1 or text[i] == pattern[j]: i += 1 j += 1 else: j = next_arr[j] if j == n: return i - j # 返回匹配起始位置 return -1 # 表示未找到匹配项 ``` 上述代码分为两部分: - `compute_next_array()` 函数用于计算模式串的部分匹配表(即`next`数组)。这部分的核心在于利用已知的最大公共前后缀长度来优化后续匹配过程[^5]。 - `kmp_search()` 函数则负责执行具体的字符串匹配逻辑。 --- ### KMP算法的应用场景 #### 文本编辑器中的查找功能 在文本编辑器中,当用户输入一段文字并希望快速定位某个关键词时,可以采用KMP算法完成这一任务。相比传统的暴力匹配方法,KMP能够在更短的时间内返回结果,尤其适用于大规模文档环境下的搜索需求[^1]。 #### 数据清洗与预处理 在大数据领域,经常需要对海量日志文件或其他形式的数据集进行过滤或提取特定字段的操作。此时如果目标子串固定不变,则可预先生成对应的`next`数组,在多次查询过程中显著提升效率[^3]。 #### 生物信息学研究 DNA序列由四种碱基组成(A,T,C,G),因此对于某些基因片段的研究工作而言,频繁涉及相似结构单元之间的对比分析。借助于KMP技术,研究人员能够更加便捷地识别出感兴趣的区域及其分布规律[^4]。 --- ### 性能优势总结 总体来看,由于引入了额外的信息存储机制——即所谓的“失败指针”,使得整个流程无需反复跳转至初始状态重新尝试;从而大幅降低了最坏情况下的时间开销,并保持相对稳定的内存占用水平[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值