kmp算法

kmp算法 编辑
本词条由“科普中国”科学百科词条编写与应用工作项目 审核 。
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
中文名 KMP算法 外文名 The Knuth-Morris-Pratt Algorithm 学 科 数据结构 应 用 字符串匹配 发现者 D.E.Knuth等 时间复杂度 O(m+n)
目录
1 简介
2 算法说明
3 优化
▪ C++代码
▪ Pascal 源代码
简介编辑
字符串的模式匹配是对字符串的基本操作之一,广泛应用于生物信息学、信息检索、拼写检查、语言翻译、数据压缩、网络入侵检测等领域,如何简化其复杂性一直是算法研究中的经典问题。字符串的模式匹配实质上就是寻找模式串P是否在主串T 中,且其出现的位置。我们对字符串匹配的效率的要求越来越高, 应不断地改良模式匹配算法,减少其时间复杂度。
KMP算法是由D.E. Knuth、J.H.Morris和V.R. Pratt提出的,可在一个主文本字符串S内查找一个词W的出现位置。此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。这个算法是由高德纳和沃恩·普拉特在1974年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终由三人于1977年联合发表。该算法减少了BF算法中i回溯所进行的无谓操作,极大地提高了字符串匹配算法的效率 [1] 。
算法说明编辑
设主串(下文中我们称作T)为:a b a c a a b a c a b a c a b a a b b
模式串(下文中我们称作W)为:a b a c a b
用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。
而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。
比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。
在第一次匹配过程中
T: a b a c a a b a c a b a c a b a a b b
W: a b a c a b
在T[5]与W[5]出现了不匹配,而T[0]T[4]是匹配的,其中T[0]T[4]就是上文中说的已经匹配的模式串子串,移动找出最长的相同的前缀和后缀并使他们重叠:
T: a b a c aa b a c a b a c a b a a b b
W: a b a c a b
然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。
然而,如果每次都要计算最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。比如:
j 0 1 2 3 4 5
W[j] a b a c a b
F(j) 0 0 1 0 1 2
当W[j]与T[j]不匹配的时候,设置j = F(j-1).
朱洪对KMP算法作了修改,他修改了KMP算法中的next函数,即求next函数时不但要求W[1,next(j)-1]=W[j-(next(j)-1),j-1],而且要求W[next(j)]<>W[j],他记修改后的next函数为newnext。显然在模式串字符重复高的情况下,朱洪的KMP算法比KMP算法更加有效。
假设在执行正文中自位置 i 起“返前”的一段与模式的自右至左的匹配检查中,一旦发现不匹配(不管在什么位置),则去执行由W[m]与t[i]+d(x)起始的自右至左的匹配检查,这里x是字符t。它的效果相当于把模式向右滑过d(ti)一段距离。显然,若ti不在模式中出现或仅仅在模式末端出现,则模式向右滑过的最大的一段距离m。图1.1示出了执行BM算法时的各种情况。实线连接发现不匹配以后要进行比较的正文和模式中的字母,虚线连接BM算法在模式向右滑后正文和模式中应对齐的字母,星号表示正文中的一个字母。
BM算法由算法1.3给出,函数d的算法由算法1.4给出。计算函数d的时耗显然是Θ(m)。BM算法的最坏情况时耗是Θ(mn)。但由于在实用中这种情况极少出现,因此BM算法仍广泛使用。以下是伪

(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。
(2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1—k个字符与开头的1—k个字符不等(或者相等但T[k]==T[j])(1≤k<j),如:T=”abCabCad” 则 next[6]=-1,因T[3]=T[6].
(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个字符与开头的k个字符相等,且T[j] != T[k] (1≤k<j)即T[0]T[1]T[2]…T[k-1]==T[j-k]T[j-k+1]T[j-k+2]…T[j-1]且T[j] != T[k].(1≤k<j);
(4) next[j]=0 意义:除(1)(2)(3)的其他情况。
C++代码

#include<stdlib.h>
#include
using namespace std;
inline void NEXT(const string&T, vector&next){//按模式串生成vector,next(T.size())
next[0] = -1;
for (int i = 1; i<T.size(); i++){
int j = next[i - 1];
while (j >= 0 && T[i - 1] != T[j]) j = next[j];//递推计算
if (j >= 0 && T[i - 1] == T[j]) next[i] = j + 1;
else next[i] = 0;
}
}
inline string::size_type COUNT_KMP(const string&S, const string&T){
//利用模式串T的next函数求T在主串S中的个数count的KMP算法
//其中T非空,
vectornext(T.size());
NEXT(T, next);
string::size_type index, count = 0;
for (index = 0; index<S.size(); ++index){
int pos = 0;
string::size_type iter = index;
while (pos<T.size() && iter<S.size()){
if (S[iter] == T[pos]){ ++iter; ++pos; }
else{
if (pos == 0) ++iter;
else pos = next[pos - 1] + 1;
}
}
if (pos == T.size() && (iter - index) == T.size()) ++count;
}
return count;
}

int main(int argc, char*argv[])
{
string S=“abaabcacabaabcacabaabcacabaabcacabaabcac”;
string T=“ab”;
//cin >> S;
//cin >> T;
string::size_type count = COUNT_KMP(S, T);
cout << count << endl;
system(“PAUSE”);
return 0;
}
Pascal 源代码

MAX_STRLEN=255;
var
next:array[1…MAX_STRLEN] of longint;
str_s,str_t:string;
int_i:longint;

procedure get_next(t:string);
var
j,k:longint;
begin
j:=1;
k:=0;
while j<length(t) do
begin
if (k=0) or (t[j]=t[k]) then
begin
inc(j);
inc(k);
next[j]:=k;
end
else
k:=next[k];
end;
end;

function kmp(s,t:string):longint;
var
i,j,back:longint;
begin
get_next(t);
back:=0;
i:=1;
j:=1;
while (i<=length(s)) and (j<=length(t)) do
begin
if (j=0) or (s[i]=t[j]) then
begin
inc(i);
inc(j);
end
else
j:=next[j];
if j>length(t) then back:=i-length(t);
end;
exit(back);
end;

begin
write(‘S=’);
readln(str_s);
write(‘T=’);
readln(str_t);
int_i:=kmp(str_s,str_t);
if int_i<>0 then
writeln(‘Found ‘,str_t,’ in ‘,str_s,’ at ‘,int_i,’.’)
else
writeln(‘Cannot found ‘,str_t,’ in ‘,str_s,’.’)
end.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值