当串p与串t匹配的时候,返回的首次匹配的位置是i-j
如果遍历完,不匹配(i=n,j<m)
:i-j>n-m
任意j,考察集合
N(p,j)={0<=t<j | p[0,t) == p[j-t,j) }
即,p[j]的前缀p[0,j)中,所有匹配的真前缀和真后缀长度为next(j)
长度为t的前缀p[0,t)与长度为t的后缀p[j-t,j)必然相等
如果i、j分别指向的元素不匹配,p[i]≠p[j]
从N(p,j)
中取第t个
元素,令p[t]
对准p[i]
这样使得前面的p[0,t)个元素就匹配了
解释:
- t=N[t](失配,一直递归,找next[next[next…]])
- N[++j]=++t;(匹配的时候,next[j+1]==next[j]+1)
其中next[j]
在p[0,j)
中最大匹配的真前缀和真后缀长度
next[j+1]≤next[j]+1
解释:下一项的最大匹配项不可能超过上一项+1
当下一项j项匹配,next[j+1]=next[j]+1
;
如果不匹配
,next[j+1]<next[j]+1一直递归,直到1+next[0]==0
(next[0]= -1
哨兵作用)
function buildNext(p){
var m=p.length,j=0;
var N=new Array(m);
N[0]=-1;
var t=N[0];
//当字符串遍历完毕
//当内部遍历到
while(j<m-1){
//匹配
if(0>j||p[j]==p[t]){
N[++j]=++t;
}else{//失配
t=N[t];
}
}
//返回构造的next()表
return N;
}
KMP找到第一个匹配的位置
//p模板字符串 t是主串
function match(p,t){
var n=t.length,i=0;
var m=p.length.j=0;
var next=buildNext(p);
while(j<m&&i<n){
if(0>j||p[j]==t[i]){//当匹配的时候,携手共进
++i;
++j;
}else{
//适配的时候找next表
j=next[j];
}
}
//遍历完主串,但是p串还未遍历完,匹配失败 返回-1
if(i-j>n-m) return -1;
return i-j;
}
变体:
p[0,j)前的一致,当比较到j和t位置的,此时出现差异,j的内容与p[0,j)内容也一样,为了不重复比较,重复递归
将buildNext()
N[++j]=++t;
变为
++t;
++j;
N[j]=p[j]!=p[t]?t:N[t];
++t;
++j;
//存在匹配的前缀 保持前缀的next不变
//不匹配改变next
N[j]=p[j]!=p[t]?t:N[t];