关于KMP算法的理解——文本匹配算法

上一篇是BF算法,虽然BF算法是简单易懂,但是其中许多冗余的遍历,使得整个效率变低。而KMP算法可以用来有效剔除其中的多说无效遍历。KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。其中实现减少匹配次数的主要方法是提前算出模板的一个部分匹配表next[]。

先举个例子吧,文本是abcabcdabdabcdabc,模板是abcdabc,

模板的匹配数组next[]表示如下:

这个代表模板的前缀和后缀的一个最大值匹配表,后续详解。

匹配的过程如下:

第1次匹配,文本a位置匹配失败,则由k - next[k - 1]得到跳跃的间隔为3 - 0 = 3。


2次匹配,文本d位置匹配失败,则由k - next[k - 1]得到跳跃的间隔为6 - 2 = 4。


3次之后的匹配,与上述相同。


上述的过程,其实精华在于如何算出它的跳跃步数,想法是前缀和后缀匹配的最大值,从模板分析:

对于1个字母,无前缀后缀,所以为0;

对于2个字母,前缀为a,后缀为b,所以为0;

对于3个字母,前缀为{a,ab},后缀为{c,bc},无匹配,为0;

对于4个字母,前缀为{a,ab,abc},后缀为{d,cd,bcd,},无匹配,为0;

对于5个字母,前缀为{a,ab,abc,abcd},后缀为{a,da,cda,bcda},a匹配,长度为1,为1;

对于6个字母,前缀为{a,ab,abc,abcd,abcda},后缀为{b,ab,dab,cdab,bcdab},ab匹配,长度为2,为2;

对于7个字母,前缀为{a,ab,abc,abcd,abcda,abcdab},后缀为{c,bc,abc,dabc,cdabc,bcdabc},abc匹配,长度为3,为3;

到这里,分析完毕,下面附上KMP算法C++版:

#include <stdio.h>
#include <iostream>

using namespace std;

void getNextFrame(string needle, int* next){
	int i = 0;  
    int j = 1; 
    int len = needle.length();  
    while(j < len)
    {
    	if(needle[i] == needle[j])
    	{
    		i++;
    		next[j] = i;
    		j++;
		}else{
			i = next[0];
			j++;
		}
	}
}

int KMPSearch(string haystack, string needle) {
	int n = haystack.size() - 1;//haystack长度 
	int m = needle.size() - 1;//needle长度 
	if(m == -1) return 0; 
	if(m > n) return -1;
    int next[needle.length()] = {0};
    getNextFrame(needle, next);
	int i = 0;
	while(needle[0] != haystack[i])
		i++;
	int j = i;
	int k = 0;
	while(i <= haystack.length() - needle.length() && j < haystack.length())
	{
		if(needle[k] == haystack[j])
		{
			if(k == needle.length() - 1)
				return i;
			k++;
			j++;
		}else{
			if(k == 0)
			{
				i = i + 1;
				k = 0;
				j = i;
			}else{
				i = i + k - next[k - 1];
				k = 0;
				j = i;
			}
		}
	}
	return -1;
}

int main(){
	string haystack = "babbbbbabb";
	string needle = "bbab";
	int firstPos = KMPSearch(haystack, needle);
	if(firstPos < 0)
		cout<<"search failed\n"<<endl;
	else
		cout<<"first appeared place:"<<firstPos<<endl;
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值