kmp算法思考和参考总结

AcWing 831. KMP字符串

所谓字符串匹配,是这样一种问题:“字符串 P 是否为字符串 S 的子串?如果是,它出现在 S 的哪些位置?” 其中 S 称为主串;P 称为模式串。下面的图片展示了一个例子。

在这里插入图片描述

暴力做法:

  • 双重循环

双重循环劣势如下:

  • 双重循环的时候,如果主串S模式串P不匹配,则如果S会上次匹配地方为i,那么本次匹配时,S串会从i+1开始匹配。
  • 对于暴力算法,如果出现不匹配字符,同时回退 SP 的指针,嵌套 for 循环,时间复杂度 O ( M N ) O(MN) O(MN),空间复杂度 O ( 1 ) O(1) O(1)。最主要的问题是,如果字符串中重复的字符比较多,该算法就显得很蠢。

比如 txt = “aaacaaab” pat = “aaab”:
在这里插入图片描述

而kmp算法有两个精髓:

  • KMP 算法永不回退 txt 的指针 i,不走回头路(不会重复扫描 txt),而是借助 next数组中储存的信息把 pat 移到正确的位置继续匹配,时间复杂度只需 O(N),用空间换时间。
  • next数组只和模式串pat有关系。

具体kmp如何优化的请看:kmp演示
具体kmp代码和next数组请看:next求解和匹配操作
最为精髓的是

然后来说明一下next数组的含义:对next[ j ] ,是pat[ 1, j ]串中前缀和后缀相同的最大长度(部分匹配值),即 pat[ 1, next[ j ] ] = pat[ j - next[ j ] + 1, j ]。

next数据应用举例
在这里插入图片描述那么next如何求出:

  • 通过对模式串和搜集
  • 类似递归,不能匹配的话,j=ne[j],再不能匹配则j=ne[ne[j]]
    //求next[]数组, i为遍历模式串的指针,j为模式串匹配相同的字母个数
    for(int i = 2, j = 0; i <= m; i++)
    {
        //不能匹配
        while(j && p[i] != p[j+1]) j = ne[j];
        //匹配上了
        if(p[i] == p[j+1]) j++;
        ne[i] = j;
    }

例题
https://www.acwing.com/problem/content/833/
在这里插入图片描述在这里插入图片描述

完整代码与定义说明:

  • ne为next数组,next在cpp中定义了
  • 模式串中使用 指针j+1 和 字母串的指针 i 作比较
#include <iostream>

using namespace std;

const int N = 100010, M = 10010; //N为模式串长度,M匹配串长度

int n, m;
int ne[M]; //next[]数组,避免和头文件next冲突
char s[N], p[M];  //s为模式串, p为匹配串

int main()
{
    cin >> n >> s+1 >> m >> p+1;  //下标从1开始

    //求next[]数组
    for(int i = 2, j = 0; i <= m; i++)
    {
        while(j && p[i] != p[j+1]) j = ne[j];
        if(p[i] == p[j+1]) j++;
        ne[i] = j;
    }
    //匹配操作
    for(int i = 1, j = 0; i <= n; i++)
    {
        while(j && s[i] != p[j+1]) j = ne[j];
        if(s[i] == p[j+1]) j++;
        if(j == m)  //满足匹配条件,打印开头下标, 从0开始
        {
            //匹配完成后的具体操作
            //如:输出以0开始的匹配子串的首字母下标
            //printf("%d ", i - m); (若从1开始,加1)
            j = ne[j];            //再次继续匹配
        }
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值