KMP讲解
Kmp算法,模式匹配。。。目前效率最高的匹配算法
朴素的算法 o(n*m)
比如 要在 abcabcabcaba 中查找子串 abcaba
朴素的算法
第一次匹配 到了箭头的地方失配了
朴素的算法接下来会从原串的第二个字符比较
于是,从箭头处开始比较,马上就失配了,但是kmp鄙视这种做法,kmp认为这次比较是可以省略的
因为kmp可以这样,在这里开始比较
Kmp可以看做是原串不动,而子串尽量往右滑,怎么滑,根据什么滑,next数组
先看next的效果,
如上例在失配后,直接从原串的当前字符c 与子串下标为2的字符比较,
2是什么?正好是子串失配点对应的next数组的数值,
因此,next数组存储的就是当失配后,下一次比较时子串的下标值。
为什么第一个值是-1呢?
第一 好求next ,第二,标记它是子串第一个字符
接下来看怎么求next数组
当下标从零开始时,对于next[i] 可以看成是
一个最大值,使S(0…x)=S(i-1-x…i-1) S(a…b)表示S的[a,b]区间的子串
这里的x就是next[i]的值,下标从一开始类似,+1即可,不再赘述。
next[i]=x
则S(0…x)=S(i-1-x…i-1)
对于next[i+1]有两种情况 先令k=i 存起来,
第一种,S(x+1)=S(i) 那么next[i+1]=next[i]+1; 很好理解
第二种 S(x+1)!=S(i) 失配了?失配? 失配了怎么办?向右滑?怎么滑?next数组 不过此时的原串和子串都是模式串
则i=next[i];不停循环 直到S(next[i]+1)=S(i);
那么 next[k]=next[i]+1; k是原来的i ,i是改变之后的值。。。 仔细读读前面几句话,就不会蒙了。。。
那么就可以写程序了。。
next的求法:
kmp怎么用next呢
上面求next的方法扩展一下,S为原串,T为子串
对于T(i),S(j),且next[i]=x,那么满足 T(0…x)=S(j-1-x…j-1)
同样分两种情况,
第一种,T(i)==S(j) ++i,++j 即匹配下一个字符,比较S(j+1)和T(i+1)的值
第二种,两者不等,i=next[i];直到满足第一种情况或者i=-1
i=-1时,已经是子串的第一个字符了,应该比较的是S(j+1) T(0) 还是T(i+1)
#include <cstdio>
#include <iostream>
using namespace std;
char a[1000];
char b[1000];
int next[1000];
void find_next(char s[],intnext[])
{
int i=0,j=-1;
next[i]=-1;
while(s[i+1])
{
if(j==-1||s[i]==s[j]){++i;++j;next[i]=j;}
else j=next[j];
}
}
int kmp(char s[],char t[],intpos)
{
int i=pos,j=0;
while((t[j]||j<0)&&s[i])
{
if(j==-1||s[i]==t[j])++i,++j;
else j=next[j];
}
if(!t[j])return i-j;
return -1;
}
int main()
{
while( cin>>a>>b)
{
find_next(b,next);
cout<<endl<<kmp(a,b,0)<<endl;
}
return 0;
}
//test
/*
abcabcabcaba abcaba
000000000001 0001
aaa b
abababa bab
abcdc cdc
*/