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; } 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); j = ne[j]; } } }
return 0; }
|