标签:相同 代码 假设 原理 就是 预处理 优化 names 首字母
字符串匹配问题在OI中比较常见,我们可以比较暴力的进行求解,这样的时间复杂度为$O(n^2)$,但这种方法并不比适用与大部分情况,因为它太慢了。于是就有三个$dalao$提出了更加快速的方法来解决这个问题。他们三个的名字的首字母分别是K、M、P,所以这种算法就简称为KMP算法。
KMP算法的时间复杂度是$O(n+m)$,甩朴素的算法十条街。但是他也比较难理解,而且题目的难度也不低,所以有些恶心。我在学习的时候没有找着多少好理解的文章。唯有阮一峰的写的好理解
所以今天写这篇随笔的主要目的是帮助大家更好的理解KMP算法。
KMP算法的核心在于一个next数组。我们先不说这个数组是干什么用的。
先来看看为什么朴素的算法时间复杂度会这么高
ABCDABDABCCA
ABDAB
对于上面这个例子,我们来模拟一下。上面是文本串,下面是模式串。
先比较第一位,然后逐位进行比较直到出现不相同的一位。
最后比较到第三位停止,因为第三位上的字符不相同
然后进行模式串的后移操作。直到开始出现相同的位。
下面是模拟的过程
这里我们进行了一共四次操作才完成。这就是高时间复杂度的根源所在
ABCDABDABCCA
ABDAB
ABCDABDABCCA
ABDAB
ABCDABDABCCA
ABDAB
ABCDABDABCCA
ABDAB
然后重复第一步第二步的操作,直到出现匹配的文本串子串位置(这里假设只是判断能不能匹配)。
再说KMP算法,KMP算法对第二步进行了优化。它预处理了一个next数组,这个数组表示的是模式串中前缀和后缀的最长的相同子串的长度
那么我们在后移模式串的时候就可以将其后移(模式串长度 - next值)位来减小时间复杂度。这个next数组就通过对模式串自己匹配自己来求
#include <iostream> #include <cstring> #include <cstdio> const int maxn = 1e6+3; using namespace std; char T[maxn], s[maxn]; int n, m, next[maxn], p; inline void Getnext() { for(int i=2; i<=m; i++) { p = next[i-1]; while(p && s[i] != s[p+1]) p = next[p]; if(s[i] == s[p+1]) next[i] = p + 1; else next[i] = 0; } } int main() { scanf("%s%s", T+1, s+1); n = strlen(T+1), m = strlen(s+1); Getnext(); p = 0; for(int i=1; i<=n; i++) { while(p && T[i] != s[p+1]) p = next[p]; if(T[i] == s[p+1]) p++; else p = 0; if(p == m) printf("%d\n", i-m+1), p = next[p]; } for(int i=1; i<=m; i++) { printf("%d ", next[i]); } return 0; }
标签:相同 代码 假设 原理 就是 预处理 优化 names 首字母
原文地址:https://www.cnblogs.com/bljfy/p/9409442.html