严蔚敏 数据结构 kmp算法详解

相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

i=3 j=3,j=nextval[3]=-1 i=9 j=5,返回 9-5=4
失败
第 2 次匹配
s=aaabaaaa b t=aaaab
成功
4.3.2
KMP算法
KMP算法是D.E.Knuth、J.H.Morris和V.R.Pratt
共同提出的,简称KMP算法。该算法较BF算法有较 大改进,主要是消除了主串指针的回溯,从而使算法效 率有了某种程度的提高。
所谓真子串是指模式串t存在某个k(0<k<j),使 得"t0t1…tk " = " tj-ktj-k+1…tj "成立。
例如,t= "abab",
即t0t1=t2t3 也就是说, “ab”是真子串。 真子串就是模式串中隐藏的信息,利用它来提高 模式匹配的效率。
一般情况:设主串s="s0s1…sn-1",模式t="t0t1…tm-1", 在进行第i趟匹配时,出现以下情况:
s: s0 s1 … si-j si-j+1 … si-1 si si+1 … sn-1 t: t0 t1 … tj-1 tj tj+1 … sm-1
第 1 次匹配 s=a b a c a b a b t=a b a b i=3 j=3 失败
此时不必从i=1(i=i-j+1=1),j=0重新开始第二次匹 配。因t0≠t1,s1=t1,必有s1≠t0,又因t0 =t2,s2=t2,所以必有 s2=t0。因此,第二次匹配可直接从i=3,j=1开始。
为此,定义next[j]函数如下:
t: t0 t1 … tk-1 tk … tj-1 tj tj+1 … sm-1
说明下一次可直接比较si和tk,这样,我们可以 直接把第i趟比较“失配”时的模式t从当前位置直 接右滑j-k位。而这里的k即为next[j]。
例如t="abab",由于"t0t1" ="t2t3"(这里k=1,j=3),则 存在真子串。设s="abacabab",t="abab",第一次匹 配过程如下所示。
既然如此,回溯到si-j+1开始与t匹配可以不做。那 么,回溯到si-j+2 开始与t匹配又怎么样?从上面推理 可知,如果 "t0t1…tj-2"≠"t2t3…tj"
仍然有
"t0t1…tj-2"≠"si-j+2si-j+3…si"
这样的比较仍然“失配”。依此类推,直到对于 某一个值k,使得: "t0t1…tk-2"≠" tj-k+1tj-k+2…tj-1"
这时,应有 "t0t1…tj-1"="si-jsi-j+1…si-1" (4.1) 如果在模式t中,
"t0t1…tj-1"≠"t1t2…tj"
(4.2)
则回溯到si-j+1 开始与t匹配,必然“失配”,理由 很简单:由(4.1)式和(4.2)式综合可知:
"t0t1…tj-1"≠"si-j+1si-j+2…si"
max{k|0<k<j,且“t0t1…tk-1”=“tj-ktj-k+1…tj-1” }
当此集合非空时
next[j]= -1 0 当j=0时 其他情况
t=“abab”对应的next数组如下:
j t[j] next[j] 0 a -1 1 b 0 2 a 0 3 b 1
void GetNext(SqString t,int next[]) { int j,k; j=0;k=-1;next[0]=-1; while (j<t.len-1)
由模式串t 求 出 next 值 的算法
{ if (k==-1 || t.data[j]==t.data[k])
/*k为-1或比较的字符相等时*/ { } else k=next[k]; j++;k++;
next[j]=k;
}
}
int KMPIndex(SqString s,SqString t) { int next[MaxSize],i=0,j=0,v; GetNext(t,next); KMP算法
这就是说,若按上述定义得到next[j]=k,而模式中
pj=pk,则为主串中字符si和pj比较不等时,不需要再和pk
进行比较,而直接和pnext[k]进行比较,换句话说,此时的 next[j]应和next[k]相同。 为此将next[j]修正为nextval[j]:
比较t.data[j]和t.data[k],若不等,则 nextval[j]=next[j]; 若相等nextval[j]=nextval[k];
b 3
第 1 次匹配
第 2 次匹配
s=aaabaaaa b t=aaaab
第 3 次匹配
s=aaabaaaa b t=aaaab
第 4 次匹配
s=aaabaaaa b t=aaaab
第 5 次匹配
s=aaabaaaa b t=aaaab
上述定义的next[]在某些情况下尚有缺陷。 例如,模式“aaaab”在和主串“aaabaaaab”匹配时, 当i=3,j=3时,s.data[3]≠t.data[3],由next[j]的指示还需 进行i=3、j=2,i=3、j=1,i=3、j=0等三次比较。实际上, 因为模式中的第1、2、3个字符和第4个字符都相等, 因此,不需要再和主串中第4个字符相比较,而可以将模 式一次向右滑动4个字符的位置直接进行i=4,j=0时的 字符比较。

"t0t1…tk-1"="tj-ktj-k+1…tj-1“ 才有
"tj-ktj-k+1…tj-1"="si-ksi-k+1…si-1"="t0t1…tk-1"
s: s0 s1 … si-j si-j+1 … si-k si-k+1 … si-1
si si+1 … sn-1
Байду номын сангаас
t: t0 t1 … tk-1 tk … tj-1 tj tj+1 … sm-1 t 右滑 j-k 位 s: s0 s1 … si-j si-j+1 … si-k si-k+1 … si-1 si si+1 … sn-1
else nextval[j]=nextval[k];
int KMPIndex1(SqString s,SqString t) { int nextval[MaxSize],i=0,j=0,v; GetNextval(t,nextval); while (i<s.len && j<t.len) { if (j==-1 || s.data[i]==t.data[j]) { i++;j++; } else j=nextval[j]; /*i,j各增1*/ /*i不变,j后退*/ 修改后的KMP 算法
else v=-1; return v; } /*返回不匹配标志*/
设主串s的长度为n,子串t长度为m。
在 KMP 算 法 中 求 next 数 组 的 时 间 复 杂 度 为
O(m),在后面的匹配中因主串s的下标不减即不回溯,
比较次数可记为n,所以KMP算法总的时间复杂度为
O(n+m)。
例如,设目标串s=“aaabaaaab”,模式串t=“aaaab”。
while (i<s.len && j<t.len)
{ if (j==-1 || s.data[i]==t.data[j]) { i++;j++; } else j=next[j]; } /*i,j各增1*/ /*i不变,j后退*/
if (j>=t.len) v=i-t.len; /*返回匹配模式串的首字符下标*/
}
if (j>=t.len) v=i-t.len; /*返回匹配模式串的首字符下标*/ else v=-1; return v; } /*返回不匹配标志*/
j
0
1
2
3
4
t[j] next[j] nextval[j]
第 1 次匹配
a -1 -1
a 0 -1
a 1 -1
a 2 -1
b 3 3
s=aaabaaaa b t=aaaab
s的长度为n(n=9),t的长度为m(m=5)。用指针i指示目
标串s的当前比较字符位置,用指针j指示模式串t的当
前比较字符位置。KMP模式匹配过程如下所示。
j
0
1
2
3
4
t[j] next[j]
a -1
a 0
s=aaabaaaa b t=aaaab
a 1
i=3
a 2
失败 j=3,j=next[3]=2 i=3 j=2,j=next[2]=1 i=3 j=1,j=next[1]=0 i=3 j=0,j=next[0]=-1 i=9 j=5,返回 10-5=4 成功 失败 失败 失败
void GetNextval(SqString t,int nextval[]) { int j=0,k=-1;
nextval[0]=-1;
由模式串t求 while (j<t.len) 出nextval值 { if (k==-1 || t.data[j]==t.data[k]) { j++;k++; (t.data[j]!=t.data[k]) if nextval[j]=k; } else k=nextval[k]; } }
相关文档
最新文档