小结(1月30日)

本文详细介绍了KMP算法,包括其核心特点(单次遍历、next数组)、next数组的生成过程、如何通过next数组避免重复比较以及实际的kmp匹配和算法优化。
摘要由CSDN通过智能技术生成

kmp (未完成)

具体题目:P3375 【模板】KMP - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

KMP算法是一种高效的字符串匹配算法,它的核心特点在于能够通过对主串进行单次遍历来实现匹配,从而达到O(n + m)的时间复杂度,其中n是主串的长度,而m是模式串的长度。

这种效率的关键在于KMP算法对模式串进行的预处理 —— 构建next数组。

NEXT数组

1.前后缀

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

2.next数组的核心概念

Next数组的核心概念在于记录模式串中每个子串的最长相同前后缀长度。具体地,对于长度为i+1的连续子串,其最长相同前后缀的长度等于 next[i] 的值

以模式串 "aabaaf" 为例,其对应的 next 数组为 [0, 1, 0, 1, 2, 0]。在这个数组中,每个元素精确地映射了模式串中相应子串的最长相同前后缀长度。例如,next[4] 对应的是模式串中前 5 个字符(即 "aabaa")的最长相同前后缀,其长度为 2,对应的前后缀为 "aa"。

3.next数组的生成

// KMP算法的next数组生成函数
void kmpnext(char *t, int *next, int n) {
    next[0] = 0; // next数组的第一个元素总是0,因为单个字符没有前后缀

    // 使用双指针技术进行遍历,l代表前缀的结束位置,r代表后缀的结束位置
    for (int l = 0, r = 1; r < n; r++) {
        // 当前后缀字符不匹配时,回溯前缀指针l到next数组的上一个位置
        while (t[l] != t[r] && l > 0) {
            l = next[l - 1];
        }

        // 当前后缀字符匹配时,递增前缀指针l
        if (t[l] == t[r] && l < r) {
            l++;
        }

        // 更新next数组的当前位置
        next[r] = l;
    }
}

在此代码段中,我们实现了通过双指针方法生成KMP算法中的next数组.。

具体来说,我们将变量i作为后缀的起始指针,而j作为前缀的起始指针。在迭代过程中,我们比较前后缀的字符,若相等,则递增j的值,这表明我们找到了一个更长的相同前后缀。如果字符不匹配,我们则依据next数组已计算出的值来调整j的位置,这是一种回溯机制,允许算法在不匹配时跳过一些不必要的比较。

4.next数组的意义

前面我们折腾这么久,通过对模式串的预处理成功生成了next数组,那么,意义何在?

其实next数组是为了跳过模式串与主串匹配中的重复比较,如下图所示

可以看出,文本串中第六个字符b 和 模式串的第六个字符f,不匹配了。如果暴力匹配,发现不匹配,此时就要从头匹配了。

但如果使用next数组,就不会从头匹配,而是从上次已经匹配的内容开始匹配,找到了模式串中第三个字符b继续开始匹配。

kmp匹配

kmp匹配的代码实现

#include <stdio.h>
#include <string.h>
#define MAX 1000 // 假设字符串的最大长度为1000

// KMP算法的next数组生成函数的声明
void kmpnext(char *t, int *next, int lent);

int main() {
    char s[MAX], t[MAX]; // s为主串,t为模式串

    // 读入主串和模式串
    scanf("%s", s);
    scanf("%s", t);

    // 计算主串和模式串的长度
    int lens = strlen(s), lent = strlen(t);

    // 定义next数组
    int next[lent];

    // 构建模式串的next数组
    kmpnext(t, next, lent);

    // KMP算法主体
    int j = 0; // 初始化模式串的索引
    for (int i = 0; i < lens; i++) { // 遍历主串
        // 当字符不匹配时,利用next数组回溯模式串的索引
        while (s[i] != t[j] && j > 0) j = next[j - 1];

        // 当字符匹配时,递增模式串的索引
        if (s[i] == t[j]) j++;

        // 完全匹配模式串时,输出匹配的起始位置
        if (j == lent) {
            printf("%d\n", i - j + 2); // 输出匹配的起始位置
            j = next[j - 1]; // 继续寻找下一个匹配
        }
    }

    // 打印next数组
    for (int i = 0; i < lent; i++) {
        printf("%d ", next[i]);
    }

    return 0;
}

在此代码段中,我们同样通过双指针方法来进行主串与模式串的匹配。

具体来说,我们将变量i作为主串的起始指针,而j作为模式串的起始指针。如果i,j指针所指向的字符匹配,j指向模式串的下一个字符,i继续遍历主串;如果i,j指针所指向的字符不匹配,将j指针指向当前next数组中对应的位置,从上次已经匹配的内容开始匹配。最后如果j指向模式串的最后一个字符,说明匹配成功,记录匹配的起始位置,同时将j指针指向当前next数组中对应的位置,从上次已经匹配的内容开始匹配,直到遍历完主串为止。

kmp算法的优化

nextval数组

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值