克努斯-莫里斯-普拉特算法

title: 算法说明.
title: 查找算法实例.
content: 让我们用一个实例来演示这个算法。在任意给定时间,本算法被两个整数codice_3和codice_4所决定:  图示如下:  我们从codice_2与codice_1的开头比较起。我们比对到codice_12时,发现codice_13与其不符。接着并不是从codice_14比较下去。我们已经知道codice_15不与codice_16相合。因此,略过这些字符,令codice_17以及codice_18。  如上所示,我们检核了codice_19这个字串。然而,这与目标仍有些差异。我们可以注意到,codice_20在字串头尾处出现了两次。这意味着尾端的codice_20可以作为下次比较的起始点。因此,我们令codice_22,继续比较。图示如下:  于codice_23的地方,又出现不相符的情况。类似地,令codice_24继续比较:  这时,codice_25不与codice_26相同,但是亦出现了两次codice_20,我们采取一贯的作法,令codice_28和codice_29,继续搜寻。  我们找到完全匹配的字串了,其起始位置于codice_30的地方。
title: 部分匹配表.
content: 部分匹配表,又称为失配函数,作用是让算法无需多次匹配codice_1中的任何字符。能够实现线性时间搜索的关键是在主串的一些字段中检查模式串的"初始字段",我们可以确切地知道在当前位置之前的一个潜在匹配的位置。换句话说,在不错过任何潜在匹配的情况下,我们"预搜索"这个模式串本身并将其译成一个包含所有可能失配的位置对应可以绕过最多无效字符的列表。  对于codice_2中的任何位置,我们都希望能够查询那个位置前(不包括那个位置)有可能的codice_2的最长初始字段的长度,而不是从codice_16开始失配的整个字段,这长度就是我们查找下一个匹配时回退的距离。因此codice_35是codice_2的可能的"适当"初始字段同时也是结束于codice_37的子串的最大长度。我们使空串长度是0。当一个失配出现在模式串的最开始,这是特殊情况(无法回退),我们设置codice_38,在下面讨论。
title: 建立表算法示例.
content: 我们首先考虑例子codice_39。使用这个大致相同的模式串作为主搜索,我们将会看到它高效的原因。  首先,我们设定codice_38。为了找到codice_41,我们必须找到一个codice_42的同时也是codice_2的前缀。但codice_42没有后缀,所以我们设定codice_45。类似地,codice_46。  继续到codice_47,我们注意到检查"所有"后缀有一个捷径:假设我们发现了一个适当后缀,结束于codice_48、长度为2(最大可能)的后缀,那么它的第一个字符是codice_2的一个适当前缀。因此一个结束于codice_50的适当前缀,我们已经确定了不可能出现在codice_51。因此在每一层递推中,这个规则是只有在上一层找到一个长度为m的有效后缀时,才需要检查给定长度为m+1的后缀(例如,codice_52)。  那么我们甚至不需要关心具有长度为2的子串,由于上一个情况因长度为1而失配,所以codice_53。  我们继续遍历到codice_54子序列,codice_55。同样的逻辑说明我们需要考虑的最长子串的长度是1,并且在codice_55这个情况中有效,回退到我们寻找的当前字符"之前"的字段,因此codice_57。  现在考虑下一个字符codice_58,codice_59,我们使用这样的逻辑:如果我们曾发现一个子模式在上一个字符codice_54之前出现,继续到当前字符codice_58,那么在它之前它本身会拥有一个结束于codice_54合适的初始段,与事实相反的是我们已经找到codice_55是最早出现在结束于codice_54的合适字段。因此为了找到codice_58的终止串,我们不需要查看codice_54。因此codice_67。  最后,我们看到codice_68下一个字符是codice_59,并且这也确实是codice_58。此外,上面的相同参数说明为了查找codice_26的字段,我们不需要向前查看codice_54,所以我们得出codice_73。  于是我们得到下面的表:  另一个更复杂和有趣的例子:
title: 建立表算法的伪代码的解释.
content: 上面的例子以最少的复杂步骤展示了组织这个表格的一般性方法。这么做的原理是对整体的搜索:大多数工作已经在检测到当前位置的时候做完了,剩下需要做的很少。略微复杂的一点是找到一个共同前后缀。这就需要有一些初始化的代码。
title: 建立表的算法的效率.
content: 建立表的算法的复杂度是codice_74,其中codice_75是codice_2的长度。除去一些初始化的工作,所有工作都是在codice_77循环中完成的,足够说明这个循环执行用了codice_74的时间,同时还会检查codice_79和codice_80的大小。在第一个分支里,codice_80被保留,而codice_79与codice_83同时递增,自然,codice_79增加了。在第二个分支里,codice_83被codice_86所替代,即以上总是严格低于codice_83,从而增加了codice_80。在第三个分支里,codice_79增加了,而codice_83没有,所以codice_79和codice_80都增加了。因为codice_93,即在每一个阶段要么codice_79增加,要么codice_79的一个下界增加;所以既然此算法只要有codice_96就终止了,这个循环必然最多在codice_97次迭代后终止,因为codice_80从codice_99开始。因此建立表的算法的复杂度是codice_74。

Summary: 在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串codice_1内查找一个词codice_2的出现位置。此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。

Algorithme de Knuth-Morris-Pratt: L'algorithme de Knuth-Morris-Pratt (ou d'une manière plus courte l'algorithme KMP) est un algorithme de recherche de sous-chaîne (de caractères), permettant de trouver les occurrences d'une chaîne formula_1 dans un texte formula_2 avec une complexité linéaire formula_3 dans le pire cas. Sa particularité réside en un pré-traitement de la chaîne, qui fournit une information suffisante pour déterminer où continuer la recherche en cas de non-correspondance. Ainsi l'algorithme ne ré-examine pas les caractères qui ont été vus précédemment, et donc limite le nombre de comparaisons nécessaires.