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数组源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值