KMP算法

KMP算法

目录

KMP算法

首先先了解一些定义

作用方式

cpp实现:

首先先了解一些定义

1.前缀:不包含尾字符的所有字串

2.后缀:不包含首字符的所有字串

3.前缀表:用于存放最长的相等的前后缀,例如:

对于字符串 s[6]=aadaad

`"a" 最大想等前后缀为"a"next[0]=1`

"aa" 最大想等前后缀为"aa" next[1]=2

"aad" 最大想等前后缀为"",因为前缀一定不存在"d",而后缀一定有,所以next[2]=0

"aada" 最大想等前后缀为"a"next[3]=1

"aadaa" 最大相等前后缀为"aa",则next[4]=2

"aadaad" 最大想等前后缀为"aad",则next[5]=3

作用方式

那么前缀表到底有什么用呢,我在学习这个的时候觉得这个想法真的泰天才啦!

先来看朴素情况会有什么问题

举一个简单的例子,子串为"abdc",主串为"abdabdc"

朴素算法下我们在对比到第四位,即在主串上从a开始,在找第四位为c时,发现主串的第四位是d而不是c.因此说明前四位并不是我们要找的子串,所以就把放在主串上的指针往后移一位,到第二个b

但是对于这个简单的例子,我们可以很容易看出,这个操作是多余的,因为对于子串"abdc",如果主串的前三位不是我要找的子串,那么前三位就不可能会有一个字符和子串的首位相等,也就是我们在做的,子串的前后缀最大相等的长度是0.

再来回顾一下最大相等前后缀是啥意思,例如上面的例子,我们子串的最大相等前后缀长度为0,就是不存在前后相等的部分,那么在主串中和子串成功匹配的前三位就可以跳过了 ! ! !,也就是说对于朴素算法,我第一次失败后要在第二位b开始继续找.而我们完全可以从第四位的"a"开始找,直接节省了两次无意义的搜索!

cpp实现:

#include <iostream>
#include <stack>
#include <string>
#include<map>
using namespace std;
const int N = 1000010;
int nex[N];
char s1[N], s2[N];
int n, m;
int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> s1 + 1 >> m >> s2 + 1;//下标从1开始
    for (int i = 2, j = 0; i <= n; i++)//i是几位的子串,j是用来计算长度的
    {
        while (j && s1[i] != s1[j + 1])j = nex[j];//若不相等,则回退j
        if (s1[i] == s1[j + 1])j++;//进位
        nex[i] = j;//保存
    }
    for (int i = 1, j = 0; i <= m; i++)
    {
        while (j && s2[i] != s1[j + 1])j = nex[j];//若和子串不相等则回退
        if (s2[i] == s1[j + 1])j++;//若相等就再进一位!!!
        if (j == n)//连着n个字符相等就是找到子串啦!
        {
            cout << i - n <<" ";
            j = nex[j];//这里并不选择归零,而是选择完整子串的最大前后缀长度,因为会出现aadaa这种对于完整子串存在重复的前后缀,对于aadaadaadaa来说就是存在三个子串(我在这卡了好久)
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值