KMP算法

如何选择算法的关键就在于先看暴力法如何做,再去想哪种算法能够优化他

KMP是什么

一种字符串匹配算法,用于查找一个模板字符串在一串长文本里每次出现的起始下标,类似于crtl+f的功能

假设有数组长文本K[N],模板P[M]

暴力法对字符串匹配:设i作为K的下标,j作为p的下标,当K[i]与 P[0]相等时,开始匹配下个字符,否则让i++,如果连续匹配到很多个字符,但突然有一个不匹配,那么i与j都会回溯,比如K[i]和P[0]相等了,一直匹配到K[i + random],P[j + random]时匹配失败,那么i,与j都会回溯,K的下标i + random回溯到i++,j + random回溯到j = 0

KMP算法就是针对这一暴力法进行优化,使得i无需回溯。通过修改j 的位置,让模式串P尽量地移动到有效的位置。

因此需要一个next数组使模式串能够快速移动,如果P[j]时失配了,那么就根据j对应的next[j] 使模式串跳跃,如果next[j] 为0 或 - 1,那么就跳到模式串开头位置,反之,则跳过next[j]个字符,继续匹配

对于如何求出next对应的值,可以看 从头到尾彻底理解KMP,串的next数组怎么算

可得到基本实现代码

int KMPsearch(char* s, char* p)
{
int i = 0, j = 0;
int slen = strlen(s);
int plen = strlen(p);

while (i slen && j < plen)
{
if (j == -1 || s[i] == p[j]) i++, j++;
else j = next[j];
}

if (j == plen) return i - l;
else return -1;
}

以及求得next数组代码

void GetNext(char* p, int ne[])
{
int plen = strlen(p);
ne[0] = -1;
int k = -1;
int j = 0;
while (j < plen - 1)
{
if (k == -1 || p[j] == p[k])
{
++k, ++j;
if (p[j] != p[k])
ne[j] = k;
else ne[j] = ne[k];
}
else k = ne[k];
}
}

具体的模式串跳跃展示

在这里插入图片描述

开始匹配时

s[0] == p[0],s[1] == p[1], 但是s[2] != p[2]

此时j = 2,看next[2]为多少,将模式串跳跃过next[2]个字符

例题代码(yxc做法)

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>

using namespace std;

const int N = 10010;
const int M = 10010;

int n, m;
char p[N], s[M];
int ne[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;

for (int i = 2, j = 0;i <= n;i++)
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j++;
ne[i] = j;
}
//kmp匹配
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) // 匹配成功
{
printf("%d ", i - n + 1);//n或j都可以
j = ne[j];
}

}
}

return 0;
}