KMP算法

KMP字符串匹配算法( Knuth–Morris–Pratt string searching algorithm, KMP algorithm ) 是由 Donald Knuth , Vaughan Pratt James H. Morris 三人设计的线性时间字符串匹配算法。

基本思想

传统的朴素匹配算法在失配之后,仅仅将模式字符串右移一个单位继续匹配。但在这些右移的过程中,多数右移是不匹配的,因此我们希望尽快的找出下一个可能的位移。例如

abcabaabcabaabcabac
||||||||||||*
abcabaabcabac

朴素匹配是仅仅右移一位

abcabaabcabaabcabac
 *
 abcabaabcabac

而理想的情况是右移6位,可以大大提高匹配的效率。

abcabaabcabaabcabac
      |||||||||||||
      abcabaabcabac

现在的问题是如何在失配时获得理想的偏移量。通过思考可以发现,从模式匹配的开始位置到失配位置,待匹配串的内容都是和模式字符串的内容相同的,因此理想的偏移量的大小实际上就是模式字符串需要右移几个单位跟已经匹配的部分的后缀匹配。由此可知,每当失配是理想偏移量的大小仅仅与模式字符串本身有关,而与待匹配字符串无关。

abcabaabcabaabcabac
||||||||||||*
abcabaabcabac
      ||||||
      abcabaabcabac

这样的理想偏移量被成为模式字符串的前缀函数,在本文的前缀函数一节中给出定义已经计算方法。根据前缀函数,我们可以在字符串失配时快速的右移模式字符串来完成字符串的查找匹配,这就是KMP算法,具体实现在本文KMP算法一节中给出。

前缀函数

模式字符串的前缀函数π用于表示模式字符串与其自身的偏移进行匹配的信息,可以用于在朴素字符串匹配算法中避免对无用偏移的监测。
π[q]是模式字符串中长为q的前缀字符串的后缀中最长前缀的长度,即π[q]=max{k|k<p且Pk ⊐Pq}.接下来的问题在于如何求出π[q].
我们可以归纳的求出π[q].

  1. 首先我们知道π[1]=0
  2. 其次,假设π[1..k]已知,我们考虑如何求出π[k+1]。
    • 若P[k+1]=P[q]则显然π[k+1]=q+1
    • 如果P[k+1]≠P[q]则根据已经获得的后缀函数q=π[k],π[π[k]],π[π[π[k]]]…不断尝试尝试P[k+1]是否和P[q+1]匹配,如果匹配则π[k+1]=q+1,否则需要继续右移尝试,直到q为0.

下面给出前缀函数计算的C++代码。

//P is start from index 1
//for example in C++, P=#abceabaabceabc
vector<int> computePrefixFunction(string &P){
	int len=P.size();
	vector<int> pi(len);
	pi[1]=0;
	for(int k=0,q=2;q<len;++q){
		while(k>0&&P[k+1]!=P[q])k=pi[k];
		k+=(P[k+1]==P[q]);
		pi[q]=k;
	}
	return pi;
}

KMP算法

根据上一节的算法求出模式字符串的前缀函数之后,根据基本思想很容易写出线性时间内字符串匹配算法,给出C++代码如下。

//S and P are both start from index 1
//for example in C++, P=#abceabaabceabc
int KMP_Macher(string &S,string &P){
	int ls=S.size(),lp=P.size();
	vector pi=computePrefixFunction(P);
	for(int q=0,i=1;i<ls;++i){
		while(q>0&&P[q+1]!=S[i])q=pi[q];
		if(P[q+1]==S[i]){
			++q;
			if(q==lp-1)//return the index of first match
				return i-lp+1;
		} else q=pi[q];
	}
	return -1;//No matching!
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值