Knuth Morris Pratt - KMP匹配算法


问题

在文本中查找模式出现的所有位置(长度为长度为都是正整数且)。

解法

没有学习AC自动机前想要理解KMP算法非常困难,KMP算法可以看作只有一个模式的AC自动机的简化版。所以将KnuthMorrisPratt放在AhoCorasickAutomata之后,请读者在学习KMP算法之前先阅读AhoCorasickAutomata。

将AC自动机应用在只有一个模式的匹配时,我们会发现这样的AC自动机中没有输出指针,只有失败指针。为了简化我们不再使用树形结构体,而用数组下标来表示失败指针:

KnuthMorrisPratt1.png

得到模式的每个节点跳转的下标,在KMP算法中,这个跳转的下标数组称为失败函数(Failure Function),或部分匹配表(Partial Match Table)。部分匹配表的实质也是最长后缀字符串。

当匹配到时,已知的最长后缀字符串为,按照AC自动机的算法,当前的匹配位置是,沿着失败指针跳转,然后继续尝试匹配。指向前缀树根节点的下标都设为

由此可得,对于,若则文本上的位置右移一位,匹配上的位置不动;若则模式上的匹配位置跳转到,文本上的位置不动。然后继续尝试匹配。对于,则文本和模式上的位置都右移一位。当为模式的末尾字符,并且匹配成功,这时我们仍然将两个位置右移一位继续匹配,那么显然有(因为模式在这个位置已经没有字符了),这时的跳转位置为,然后就可以正常匹配了。

根据AC自动机中构造前缀树及失败指针的算法可知:

对于模式上的位置(前缀树根节点的第一层孩子节点),其失败指针为

对于模式上的位置,其父节点位置为,父节点的失败指针位置为,而失败指针的孩子节点的位置必然是。若,则可知失败指针为;否则失败指针为

即公式:

实际编程中为了方便操作数组下标,通常会定义数组,令

KMP算法的时间复杂度为


源码

KnuthMorrisPratt.h

KnuthMorrisPratt.cpp

测试

KnuthMorrisPrattTest.cpp