1 简介
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。(以上信息来自百度百科)如果初次看不是太理解的话请无视,随着下面的讲解一切都会变得更加清晰。
2 传统字符串匹配算法
问题:假设已知字符串s和p,现在要在字符串s中查找字符串p
思路:首先从s的第一个字符开始往后与字符串p进行匹配,匹配失败后回溯到s第二个字符,再与字符串p进行匹配,失败后s再回溯到下一个字符位置,依次向下,直到匹配成功或走完字符串s;
int kmp(string &s,string &p)
{
int i=0,j=0;
if(s.length()<1||p.length()<1)
{
return -1;
}
while(i<s.length()&&j<p.length())
{
if(s[i]==p[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
return i==p.length()?i-j:-1;
}
3 kmp算法
1 求解next数组
首先我们要理解以下几个概念
最大相同缀:一个字符串长度最大的相同前缀后缀 (自己起的名字,由表述很容易理解)
不过还是举个例子吧 比如字符串ABDABCABD最大相同缀为ABD 然后我们:
把长度为n的字符串拆成n个字符串 下面我们来寻找这n个字符串的最大相同缀长度 我们用next[]存储
例:字符串ABCDABD
j 0 1 2 3 4 5 6
A B C D A B D
next 0 0 0 0 1 2 0
现在我们把next数组后移一位,首位变成-1,得到新的next数组
next -1 0 0 0 0 1 2
下面 详解next数组求解方式()
首先 next[0]=-1;
已知next[i]=j 求next[i+1] //next[5]=1;(由next[5]求next[6])
如果p[i]=p[j] next[i+1]=next[i]+1=j+1; //p[5]=p[1];next[6]=2;
//next[6]=2;p[6]!=p[2](由next[6]求next[7])//尽管这里next[7]并没有意义,无视吧
否则 令j=next[j] 因为可能存在更小的串 //j=next[2]=0;
继续比较
int getnext(string &p)
{
int i=0,j=-1;<pre name="code" class="cpp"> next[0]=-1;
while(i<p.length())
{
if(j==-1||p[i]==p[j])//对于每个i这里得到i+1的next[]
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
现在找到了next数组,我们就可以开始kmp算法之旅了。
2.利用next数组进行快速匹配
假使现在要在字符串s:BBC ABCDAB ABCDABCDABDE中查找字符串p:ABCDABD只要进行以下步骤即可:
初始化 i=j=0;
1:如果j == -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
2: 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。
int kmp(string &s,string &p)
{
getnext(p);//获取next数组值</span>
int i=0,j=0;
while(i<s.length()&&j<p.length())
{
if(j==-1||s[i]==p[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
return j==p.length()?i-j:-1;//返回起始位置
}
对下面这一段没有兴趣的可以自行跳过
*************************************************************
下面我们把上面的例子按思路走一遍
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
失配,j=next[j]=-1;满足条件1;i++,j++; (i=1,j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;
i++,j++;(i=2,j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;i++,j++;(i=3,j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
再次失配,满足条件2;j=next[j]=-1;满足1;i++,j++;(i=4,j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配成功,满足1,i++;j++; (i=5;j=1)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配成功,满足1,i++;j++; (i=6;j=2)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
......
匹配成功,满足1,i++;j++; (i=9;j=6)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配失败,j=next[j] (i=9;j=2)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配失败,j=next[j] (i=9;j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配失败,j=next[j] (i=9;j=-1)
满足1,i++;j++ (i=10;j=0)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
......
满足1,i++;j++ (i=16;j=6)
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
......
s:BBC ABCDAB ABCDABCDABDE
p: ABCDABD
匹配完成