KMP算法

引言

字符串匹配问题在OI中比较常见,我们可以比较暴力的进行求解,这样的时间复杂度为$O(n^2)$,但这种方法并不比适用与大部分情况,因为它太慢了。于是就有三个$dalao$提出了更加快速的方法来解决这个问题。他们三个的名字的首字母分别是K、M、P,所以这种算法就简称为KMP算法。

 

KMP算法

KMP算法的时间复杂度是$O(n+m)$,甩朴素的算法十条街。但是他也比较难理解,而且题目的难度也不低,所以有些恶心。我在学习的时候没有找着多少好理解的文章。唯有阮一峰的写的好理解

所以今天写这篇随笔的主要目的是帮助大家更好的理解KMP算法。

 

实现原理

KMP算法的核心在于一个next数组。我们先不说这个数组是干什么用的。

先来看看为什么朴素的算法时间复杂度会这么高

 

ABCDABDABCCA

ABDAB

对于上面这个例子,我们来模拟一下。上面是文本串,下面是模式串。

 

step 1

先比较第一位,然后逐位进行比较直到出现不相同的一位。

最后比较到第三位停止,因为第三位上的字符不相同

 

step 2

然后进行模式串的后移操作。直到开始出现相同的位。

下面是模拟的过程

这里我们进行了一共四次操作才完成。这就是高时间复杂度的根源所在

ABCDABDABCCA

 ABDAB

 

ABCDABDABCCA

  ABDAB

 

ABCDABDABCCA

   ABDAB

 

ABCDABDABCCA

    ABDAB

 

step 3

然后重复第一步第二步的操作,直到出现匹配的文本串子串位置(这里假设只是判断能不能匹配)。

 

再说KMP

再说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;
}

  

 

posted @ 2018-08-02 19:49  Mystical-W  阅读(324)  评论(0编辑  收藏  举报