记号约定及符号说明:
F :fullString,
S:subString
n = length(F)
m = length(S)
1. 朴素的字符串匹配算法
朴素的模式匹配的基本思想为:从主串F的第一个字符开始和S的第一个字符比较,如果相同则比较两者的后续字符,否则从F的第二个字符开始重新和S的第一个字符比较,以此类推,直到S和F的一个子串相等,则称为匹配成功,否则匹配失败。
容易证明朴素字符串匹配算法的平均时间复杂度为O(mn)。
2. KMP字符串匹配算法
KMP算法的基本思想是:在遇到未能完全匹配的时候,充分利用当前已知的信息,使下一步的匹配测试跳过尽可能多的不可能匹配成功的字符,减少多余的测试和比较。
便于描述上的简洁,以下设定字符串的起始下标从1开始,在使用C语言等数组下标从0开始的语言实现该算法时,只需将所有数组下标均减1即可。
当一次匹配失败后,得到一个p(1≤p<m),使得F[i+1..i+p]=S[1..p]且F[i+p+1]≠S[p+1]。
于是我们希望找到一个最小的整数j(i+1≤j≤i+p),使得
F[j+1..j+p]=S[1..q] (1)
其中j+q=i+p,这样下一次比较就可以跳过F[i+1..j]这一段,直接从F[j+1]开始。
因此,问题的关键在于找到一个这样的j。
由于j+q=i+p,j>i,从而q<p。
分析一下q的含义,F[i+1..i+p]=S[1..p]且F[j+1..j+q]=S[1..q], 所以S[1..q]是S[1..p]的后缀。
由于j是满足(1)式的最小整数,从而S[1..q]是S[1..p]的最长后缀。
同时注意到S[1..q]是S[1..p]的前缀,从而q的直完全由S及p确定,即q=π(S,p),其中
π称为前缀函数,其定义为
π:{S,{1,2,...,m}}→{0,1,...,m-1},且满足
π(S,p)=max{k:0<=k<p且S[1..k]是S[1..q]的后缀}