洛谷 P3375 【模板】KMP

建议搭配着董晓算法的视频观看此题解。视频链接:F03【模板】KMP 算法_哔哩哔哩_bilibili

声明:本题解在董老师讲解的基础上再进行细分,如有看不懂的地方,请在评论区提问或者私信我。如图文有侵权,请联系我删除

题目链接:【模板】KMP - 洛谷​​​​​​

我们先从一个样例去代入这道题。这里假设S是主串,P是模式串,求P串在S串中出现的位置。(PS:这里字符串的下标从1开始

那么我们先考虑暴力算法,肯定是S和P一个一个的对比。像这样

一直对比到出现第一个不相同的字母的时候。这个时候嘛肯定是,i要回到S的第二个位置,然后j回到第一个位置,像这样

我们想想都知道,这时间复杂度太高了(这里假设S的长度是m,P的长度是n,那么时间复杂度就是O(nm))非常的高。并且你想想,前面S和P都有这么多已经匹配的字母了,就因为一个不相同的字母就导致了j从9回到1,i从9回到了2,这太可惜了吧。

那么KMP算法就是为了解决这样的问题,让浪费最小化

KMP算法呢,我建议还是看看董老师讲解的,我在这里只解释一下视频中的一些难点

我这里只解释一下为什么每次P[i]!=P[j+1]的时候,j需要等于ne[j]。


首先我们知道next[i]表示的是模式串P[1,i]中相等前后缀的最长的长度(图片中ne数组即是next数组)。

我们以右边中间的那幅图来举例子(i=8,j=5的那幅图)。在这幅图中,我们已经找出了相等前后缀的最长长度,那么当i增加到9的时候,这个时候P[9]=a,我们在看P[j+1](j==5)即P[6]=b,

这个时候P[9]!=P[6],那么说明此时相等前后缀的长度不能+1,那么只能减少,这一点是毋庸置疑的。接来下就是重点:那么上面新加进来的字母a,会导致此时的相等前后缀长度减少,那么下面的aabaa想要维持原来的状态,原来是什么状态呢,就是下面的aabaa和上面的aabaa,那个时候的相等前后缀长度是5,但是你上面新加进的a肯定会破坏原本的状态,此时下面的aabaa想要维持原来的长度,或者说让自己减小的小一点。或者说此时上面的aabaa和下面的aabaa不匹配了,因为上面的aabaa的下一个和下面的aabaa的下一个不匹配嘛,所以这个时候下面的aabaa的最后一个a就要跳回到一个匹配的位置,这个时候我们在看一下ne[i]的定义,他表示模式串P[1,i]中的相等前后缀的长度,所以j要回跳到ne[j]的位置,因为这个位置是上面的aabaa和下面的aabaa,或者说不放弃已经匹配了这多的字符嘛,然后这个时候再看ne[j]+1的位置是不适和上面的aabaa的下一个位置匹配,这就是为什么j要跳回到ne[j]的原因,说得有点抽象,希望大家可以补充一下,或者有疑问的可以再评论区留言,我有时间的话会回复的!

最后放出代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1000010;
int m,n;
char S[N],P[N];
int ne[N];

int main()
{
    cin>>(S+1)>>(P+1);
    m = strlen(S+1),n = strlen(P+1);
    ne[1] = 0;
    for(int i = 2,j = 0;i<=n;i++)//next函数计算
    {
        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<=m;i++)
    {
        while(j && S[i]!=P[j+1]) j = ne[j];
        if(S[i] == P[j+1]) j++;
        if(j == n) cout<<i-n+1<<endl;
    }
    for(int i = 1;i<=n;i++) cout<<ne[i]<<' ';
}

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

算法好玩头秃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值