Maison >interface Web >js tutoriel >Laissez-vous comprendre facilement l'algorithme KMP
L'algorithme KMP (l'algorithme de Knuth-Morris-Pratt) est utilisé pour la correspondance de chaînes afin de trouver une sous-chaîne donnée à partir d'une chaîne. Mais ce n’est pas très simple à comprendre et à maîtriser. Comprendre la table de correspondance partielle dans son concept est la clé pour comprendre l'algorithme KMP. La discussion ici évite la logique obscure qui la sous-tend et se concentre sur sa compréhension à partir de son application. Recherche de chaînePar exemple, recherchez la sous-chaîne Une solution simple, nous pouvons faire ceci,
L'inconvénient de cette solution simple est qu'à chaque fois que la correspondance échoue, l'index ne recule que d'une position, ce qui comporte de nombreuses opérations redondantes et n'est pas efficace. Au premier tour de correspondance, c'est-à-dire lorsque l'indice est 0, nous pouvons constater que les quatre premiers caractères Table de correspondance partielle/Table de correspondance partielle En prenant une chaîne de longueur 8 <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">char: | a | b | a | b | a | b | c | a |<br>index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | <br>value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |</span> La ligne Sous-ensemblePour l'exemple de chaîne ci-dessus, si nous observons la position où Préfixe et suffixePour une chaîne donnée, supprimez un ou plusieurs caractères de la fin, et la partie restante est appelée la vraie valeur du préfixe de chaîne (propre préfixe), ci-après dénommé préfixe. "Vrai" ici ne signifie pas "vrai préfixe". Pensez au "sous-ensemble approprié" d'un ensemble en mathématiques. Par exemple,
De même, en commençant par l'en-tête, supprimez un ou plusieurs mots, et la partie restante est le vrai suffixe (Suffixe approprié) de la chaîne. Ou
Valeur de correspondance partielleOn peut voir que tous les préfixes et suffixes sont symétriques en quantité, nous pouvons alors en trouver un dans le préfixe pour correspondre au suffixe, d'abord Ne je me soucie de l’intérêt de faire ce match. Prenons le texte initial Si l'on observe la position où
Faites correspondre les préfixes du suffixe à tour de rôle. Ici, le seul sous-caractère pouvant correspondre dans la liste des suffixes est <.> La longueur de la chaîne est de 1, alors remplissez le résultat de l'observation dans le tableau et notez-le, qui correspond au tableau de correspondance partielle que vous avez vu au début. est 3. La sous-chaîne obtenue à ce moment est
, La longueur est 2, ce qui est également cohérent avec la valeur du tableau de correspondance partielle ci-dessus. est 5. A ce moment, la sous-chaîne est
<span style="font -family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅é»', Tahoma, Arial, sans-serif">ab<li> code><code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">ab</span>
abab<span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">abab</span>
, sa longueur est de 4. soit 6, la sous-chaîne est . On s'attend également à ce que puisque tous les préfixes commencent par Utilisation d'une table de correspondance partielleEn utilisant la valeur de correspondance partielle ci-dessus, lorsque nous effectuons une recherche de chaîne, nous n'avons pas besoin de nous déplacer d'un seul bit après chaque échec, mais plusieurs bits peuvent être déplacés pour supprimer certaines correspondances redondantes. Il y a ici une formule comme suit :
如果匹配过程中,匹配到了部分值为 下面是本文开始时的那个部分匹配表: <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">char: | a | b | a | b | a | b | c | a |<br>index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | <br>value: | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |</span> 假设需要从 首次匹配发生在总字符串的第二个字符, <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bacbababaabcbab |<br> abababca</span> 此时匹配的长度为 1,部分匹配表中索引为 1-1=0 的位置对应的部分匹配值为 0,所以我们可以向前移动的距离是 继续直到再次发生匹配,此时匹配到的情况如下: <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bacbababaabcbab |||||<br> abababca</span> 现在匹配到的长度是 5,部分匹配表中 5-1=4 对应的部分匹配值为 3,所以我们可以向前移动 5-3=2,此时一下子就可以移动两位了。 <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif"> 上一次的位置 | 最新移动到的位置 | |bacbababaabcbab<br> xx|||<br> abababca</span> 此时匹配到的长度为 3, 查找到 3-1=2。 <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bacbababaabcbab<br> xx|<br> abababca</span> 此时我们需要查找的字符串其长度已经超出剩余可用来匹配的字符串了,所以可直接结束匹配,得到结论:没有查找到结果。 Javascript 中的实现以下是来自 trekhleb/javascript-algorithms 中 JavaScript 版本的 KMP 算法实现: 相关教程:Javascript视频教程 <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">//**<br/> * @see https://www.youtube.com/watch?v=GTJr8OvyEVQ<br/> * @param {string} word<br/> * @return {number[]}<br/> */<br/>function buildPatternTable(word) {<br/> const patternTable = [0];<br/> let prefixIndex = 0;<br/> let suffixIndex = 1;<br/><br/> while (suffixIndex < word.length) {<br/> if (word[prefixIndex] === word[suffixIndex]) {<br/> patternTable[suffixIndex] = prefixIndex + 1;<br/> suffixIndex += 1;<br/> prefixIndex += 1;<br/> } else if (prefixIndex === 0) {<br/> patternTable[suffixIndex] = 0;<br/> suffixIndex += 1;</span><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif"><br/></span><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif"> } else {<br/> prefixIndex = patternTable[prefixIndex - 1];<br/> }<br/> }<br/><br/> return patternTable;<br/>}<br/><br/>/**<br/> * @param {string} text<br/> * @param {string} word<br/> * @return {number}<br/> */<br/>export default function knuthMorrisPratt(text, word) {<br/> if (word.length === 0) {<br/> return 0;</span><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif"><br/> }<br/><br/> let textIndex = 0;<br/> let wordIndex = 0;<br/><br/> const patternTable = buildPatternTable(word);<br/><br/> while (textIndex < text.length) {<br/> if (text[textIndex] === word[wordIndex]) {<br/> // We've found a match.<br/> if (wordIndex === word.length - 1) {<br/> return (textIndex - word.length) + 1;<br/> }<br/> wordIndex += 1;<br/> textIndex += 1;<br/> } else if (wordIndex > 0) {<br/> wordIndex = patternTable[wordIndex - 1];<br/> } else {<br/> wordIndex = 0;<br/> textIndex += 1;<br/> }<br/> }<br/><br/> return -1;<br/>}<br/></span> 时间复杂度 因为算法中涉及两部分字符串的线性对比,其时间复杂度为两字符串长度之和,假设需要搜索的关键词长度为 k,总字符串长度为 m,则时间复杂度为 O(k+m)。 |
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!