基本思想

目标(target):主串
模式(pattern):要在主串中寻找的子串
从左往右将模式的每个字符和对应位置的目标字符比较,在模式的第j位不匹配称为失配,则模式中失配位置之前的子串P0P1...Pj1P_0P_1...P_{j-1}全部匹配上:
T:T0T1...T:T_0T_1...TsTs+1...Ts+j1T_sT_{s+1}...T_{s+j-1}Ts+jT_{s+j}...Tn1...T_{n-1}
PP:      P0P1P_0P_1...Pj1...P_{j-1} PjP_j...Pm...P_m
若此时在模式P中存在最大的kk,使得P0...Pk=Pjk1...Pj1P_0...P_k=P_{j-k-1}...P_{j-1},即模式PP中有首尾重叠的部分,则下次比较可以将模式P向后移动jk1j-k-1位:
T:T0T1...T:T_0T_1...TsTs+1...T_sT_{s+1}...Ts+jk1...Ts+j1T_{s+j-k-1}...T_{s+j-1}Ts+jT_{s+j}...Tn1...T_{n-1}
PP:      P0P1P_0P_1......Pjk1P_{j-k-1}...Pj1...P_{j-1}PjP_j...Pm...P_m
PP:          P0P_0   ...Pk...P_{k}  Pk+1...Pj...PmP_{k+1}...P_j...P_m
kk的极大性可以保证将模式P向后移动少于jk1j-k-1位不会匹配,因为若匹配上了,意味着有更大的kk满足P0...Pk=Pjk1...Pj1P_0...P_k=P_{j-k-1}...P_{j-1},从而破坏了kk的极大性。

由上述分析可知对于某个jjkk的取值只和模式PP有关,从而对于固定的PPkkjj的函数,记k=next[j]k=next[j]next[j]next[j]表达式,后续给出其求法:
next[j]={1j=0k+1k0k<j1P0...Pk=Pjk1...Pj10else next[j]=\begin{cases} -1 &\text{} j=0 \\ k+1 &\text{} k为满足0\leqslant k<j-1且P_0...P_k=P_{j-k-1}...P_{j-1}的最大整数 \\ 0 &\text{else } \end{cases}

算法流程

startstart:从目标TTstartstart(从0开始)处开始匹配模式PP
len_matchlen\_matchPP中已经匹配上的长度,也即PP进行匹配的起始位置,初始为0
length_targetlength\_targetTT的长度
length_patternlength\_patternPP的长度
nextnext:数组,表示PPnextnext向量
1、若length_pattern+start>length_targetlength\_pattern + start> length\_target则返回-1(表示匹配失败)
2、从左到右逐一比较模式Plen_match...PmP_{len\_match}...P_m和目标的字符,若全部匹配则返回目标匹配的起始位置startstart;否则假设直到PjP_j不匹配:
  若next[j]=1next[j]=-1(即j=0j=0,第一个字符就匹配失败),则startstart自增,转1;
  否则将startstart增至PP与原来的PPnext[j]next[j]个重合,根据next[j]next[j]定义,重合部分TTPP全部匹配,从而只需要从下一个位置,即next[j]next[j]开始继续比较,,故令len_match=next[j]len\_match=next[j],转1。
例如T=acabaabaabcacaabc,P=abaabcacT=acabaabaabcacaabc,P=abaabcac时的匹配过程如下:
kmp算法

next向量计算

由定义next[0]=1next[0]=-1,表示P0P_0失配时startstart需要向后移动1位重新开始匹配。
next[1]=0next[1]=0(定义中的else情况)。
对于失配位置jjnext[j]next[j]的意义为P0P1...Pj1P_0P1...P_{j-1}首尾能重叠的最大长度,即满足P0...Pk=Pjk1...Pj1P_0...P_k=P_{j-k-1}...P_{j-1}的最大的k+10k<j1k+1,0\leqslant k<j-1,若不存在重叠部分即为0。
为了求next[j]next[j],可以考虑其与next[j1]next[j-1]的关系。
1、设k=next[j1]j>1k=next[j-1],j>1,则由next[j1]next[j-1]的意义有P0...Pk1=Pjk1...Pj2P_0...P_{k-1}=P_{j-k-1}...P_{j-2}
P0P1P_0P_1......Pjk1P_{j-k-1}...Pj2...P_{j-2}Pj1Pj...PmP_{j-1}P_j...P_m
     P0P_0   ...Pk1...P_{k-1}PkP_k...Pj...Pm...P_j...P_m
2、若Pk=Pj1P_k=P_{j-1},则有P0...Pk=Pjk1...Pj1P_0...P_k=P_{j-k-1}...P_{j-1},且此kk具有极大性,从而next[j]=next[j1]next[j]=next[j-1],返回。
3、若PkPj1P_k\neq P_{j-1},则需要寻找最大的kk'使得P0...Pk=Pjk1...Pj1P_0...P_{k'}=P_{j-k'-1}...P_{j-1}成立:
P0P1P_0P_1......Pjk1...Pjk1...Pj2P_{j-k-1}...P_{j-k'-1}...P_{j-2}Pj1P_{j-1}Pj...PmP_j...P_m
     P0P_0   .................Pk1.................P_{k-1}PkP_k...Pj...Pm...P_j...P_m
          P0P_0   ...Pk1...P_{k'-1}PkP_{k'}...Pm...P_m
于是有P0...Pk1=Pkk...Pk1P_0...P_{k'-1}=P_{k-k'}...P_{k-1},即k=next[k]k'=next[k]
4、若k=1next[j]=0k=kk'=-1,则next[j]=0,否则令k=k',转2.
例如P=abaabcacnextP=abaabcac的next向量计算过程如下:
kmp算法

代码

代码在这里

图片来源

例子的图片来自清华大学殷人昆和王宏的数据结构电子教案。

相关文章:

  • 2021-11-28
猜你喜欢
  • 2021-07-26
  • 2021-11-21
  • 2021-12-02
  • 2022-01-16
  • 2021-05-05
相关资源
相似解决方案