Heim > Web-Frontend > js-Tutorial > Damit Sie den KMP-Algorithmus leicht verstehen

Damit Sie den KMP-Algorithmus leicht verstehen

little bottle
Freigeben: 2019-04-30 14:25:40
nach vorne
2075 Leute haben es durchsucht

Der KMP-Algorithmus (Knuth-Morris-Pratt-Algorithmus) wird für den String-Abgleich verwendet, um einen bestimmten Teilstring aus einem String zu finden. Aber es ist nicht ganz einfach, es zu verstehen und zu meistern. Das Verständnis der partiellen Matching-Tabelle in ihrem Konzept ist der Schlüssel zum Verständnis des KMP-Algorithmus.

Die Diskussion hier vermeidet die obskure Logik dahinter und konzentriert sich darauf, sie anhand ihrer Anwendung zu verstehen.

String-Suche

Suchen Sie beispielsweise die Teilzeichenfolge abcdef aus der Zeichenfolge abcdg.

Eine einfache Lösung:

  • Nehmen Sie jeweils die erste Ziffer zum Abgleich heraus, und wenn sie gleich sind, nehmen Sie die zweite Ziffer heraus.
  • Wenn sie unterschiedlich sind, verschieben Sie den Index beginnend bei der zweiten Ziffer der Gesamtzeichenfolge um ein Bit zurück und wiederholen Sie Schritt eins.

Der Nachteil dieser einfachen Lösung besteht darin, dass sich der Index jedes Mal, wenn die Übereinstimmung fehlschlägt, nur um eine Position zurückbewegt, was viele redundante Vorgänge mit sich bringt und nicht effizient ist.

In der ersten Matching-Runde, also wenn der Index 0 ist, können wir feststellen, dass die ersten vier Zeichen abcd gleich sind, aber später stellen wir fest, dass das gewünschte g nicht mit dem übereinstimmt real e. Es zeigt an, dass die Übereinstimmung fehlschlägt, wenn der Index 0 ist, und wir beginnen mit der Betrachtung des Index 1. Da wir jedoch bereits das Auftreten der ersten vier Zeichen in der Gesamtzeichenfolge in der ersten Übereinstimmungsrunde kennen, Wir müssen sie immer noch einzeln und wiederholt abgleichen.

Partielle Übereinstimmungstabelle/Partielle Übereinstimmungstabelle

Nehmen wir als Beispiel eine Zeichenfolge der Länge 8 abababca, lautet die teilweise Übereinstimmungstabelle:

<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>
Nach dem Login kopieren
Nach dem Login kopieren

Die Zeile value ist der Wert der teilweise übereinstimmenden Tabelle.

Teilmenge

Wenn wir für die obige Beispielzeichenfolge die Position beobachten, an der index 2 ist, dann erhalten wir eine Teilmenge der Zeichenfolgeaba, wenn Wenn wir die Position beobachten, an der index 7 ist, ist es offensichtlich, dass wir die gesamte Zeichenfolge erhalten. Wenn die Position, die wir beobachten, unterschiedlich ist, bedeutet dies, dass die Teilmenge der Zeichenfolge, auf die wir achten, unterschiedlich ist, weil sich die Teilzeichenfolge geändert hat.

Präfix und Suffix

Entfernen Sie für eine bestimmte Zeichenfolge ein oder mehrere Zeichen vom Ende, und der verbleibende Teil wird als wahrer Wert des Zeichenfolgenpräfixes (Proper) bezeichnet Präfix), im Folgenden Präfix genannt. „Wahr“ bedeutet hier nicht „wahres Präfix“. Denken Sie an die „eigentliche Teilmenge“ einer Menge in der Mathematik. Beispielsweise lautet das Präfix banana:

  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica , Helvetica Neue, 微软雅é»', Tahoma, Arial, sans-serif">b<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">b</span>
  • ba<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">ba</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅é»' , Tahoma , Arial, sans-serif">ban<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">ban</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB , Helvetica, Helvetica Neue, 微软雅é»', Tahoma, Arial, sans-serif">bana<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bana</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅é»', Tahoma, Arial, sans-serif">banan<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">banan</span> code>

In ähnlicher Weise entfernen Sie ausgehend von der Kopfzeile ein oder mehrere Wörter, und der verbleibende Teil ist das wahre Suffix (richtiges Suffix) der Zeichenfolge. Oder banana, sein Suffix ist:

  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">anana</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">nana</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">ana</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">na</span>
  • <span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">a</span>

Teilweiser Übereinstimmungswert

Es ist ersichtlich, dass alle Präfixe und Suffixe mengenmäßig symmetrisch sind. Dann können wir eines aus dem Präfix finden und es mit dem Suffix abgleichen Der Sinn dieser Verbindung ist mir egal. Nehmen Sie als Beispiel den Anfangstext abababca.

Wenn wir die Position beobachten, an der index 2 ist, ist die Teilzeichenfolge aba und ihre Suffixe und Suffixe sind:

  • Präfix: a, ab
  • Suffix: ba, a

Passen Sie die Präfixe im Suffix der Reihe nach an. Das einzige Unterzeichen, das in der Suffixliste übereinstimmen kann, ist a Die Länge der Zeichenfolge beträgt 1. Tragen Sie also das Beobachtungsergebnis in die Tabelle ein und notieren Sie es, was mit der Tabelle mit teilweiser Übereinstimmung übereinstimmt, die Sie am Anfang gesehen haben.

Sehen Sie sich als weiteres Beispiel die Position an, an der index 3 ist. Die zu diesem Zeitpunkt erhaltene Teilzeichenfolge ist abab, und das Präfix und Suffix zu diesem Zeitpunkt sind:

  • Präfix: a, ab, aba
  • Suffix: bab, ab, b

Zu diesem Zeitpunkt kann es beobachtet werden dass das übereinstimmende Element ab ist. Die Länge beträgt 2, was auch mit dem Wert in der Tabelle mit teilweiser Übereinstimmung oben übereinstimmt.

Beobachten wir als weiteres Beispiel die Position, an der index 5 ist. Zu diesem Zeitpunkt ist die Teilzeichenfolge ababab und das Suffix und das Suffix sind:

  • Präfix: a, ab , aba, abab, ababa
  • Suffix: babab, abab, bab, ab, b

dann nimm Jedes Element im Präfix wird mit dem Element im Suffix abgeglichen und schließlich werden zwei passende Elemente gefunden,

  • <span style="font -family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅é»', Tahoma, Arial, sans-serif">ab<code style="white-space: nowrap;"><span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">ab</span> code>
  • abab<span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">abab</span>

Wir nehmen die längere abab, ihre Länge beträgt 4.

Schauen wir uns nun die obige Teilübereinstimmungstabelle an. Erstens können wir verstehen, woher ihr Wert kommt, und zweitens können wir die Bedeutung ihrer Darstellung verstehen, d. h. die längste Länge unter allen Übereinstimmungen von Präfixen und Suffixen.

Wenn wir fortfahren, bis index 6 ist, ist die Teilzeichenfolge abababc und vorhersehbar wird keine Übereinstimmung im Präfix und Suffix gefunden. Weil nicht alle Präfixe c enthalten und alle Suffixe c enthalten. Daher ist der Wert der teilweisen Übereinstimmung zu diesem Zeitpunkt 0.

Wenn Sie fortfahren, erreichen Sie das Ende der Zeichenfolge, also die gesamte Zeichenfolge abababca. Da alle Präfixe mit a beginnen und alle Suffixe mit a enden, wird außerdem erwartet, dass der Teilübereinstimmungswert zu diesem Zeitpunkt mindestens 1 beträgt. Sie werden weiterhin feststellen, dass, da den folgenden Suffixen c hinzugefügt wird, alle Suffixe ca enthalten und das einzige Präfix, das c enthalten kann, abababc ist und die Länge von 7 dieselbe ist wie die Suffix bababca gleicher Länge stimmt nicht überein. An diesem Punkt kann der Schluss gezogen werden, dass das Übereinstimmungsergebnis 1 ist und keine Übereinstimmung mehr vorliegt.

Verwendung einer teilweisen Übereinstimmungstabelle

Wenn wir den oben genannten Wert für die teilweise Übereinstimmung verwenden, müssen wir bei der Durchführung einer Zeichenfolgensuche nicht nach jedem Fehler nur ein Bit verschieben. Es können jedoch mehrere Bits verschoben werden, um einige redundante Übereinstimmungen zu entfernen. Hier gibt es eine Formel wie folgt:

Wenn eine teilweise Übereinstimmung der Länge „partial_match_length“ gefunden wird und Tabelle[partial_match_length] > Zeichen.

如果匹配过程中,匹配到了部分值为 partial_match_length,即目前找出前 partial_match_length 个字符是匹配的,将这个长度减一作为部分匹配表格中的 index 代入,查找其对应的 valuetable[partial_match_length-1],那么我们可以向前移动的步长为 partial_match_length - table[partial_match_length - 1]

下面是本文开始时的那个部分匹配表:

<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>
Nach dem Login kopieren
Nach dem Login kopieren

假设需要从 bacbababaabcbab 中查找 abababca,根据上面的公式我们来走一遍。

首次匹配发生在总字符串的第二个字符,

<span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bacbababaabcbab |<br> abababca</span>
Nach dem Login kopieren

此时匹配的长度为 1,部分匹配表中索引为 1-1=0 的位置对应的部分匹配值为 0,所以我们可以向前移动的距离是 1-0 1。其实也相当于没有跳跃,就是正常的本次匹配失败,索引后移一位的情况。这里没有节省任何成本。

继续直到再次发生匹配,此时匹配到的情况如下:

<span style="font-family:Microsoft Yahei, Hiragino Sans GB, Helvetica, Helvetica Neue, 微软雅黑, Tahoma, Arial, sans-serif">bacbababaabcbab    |||||<br>    abababca</span>
Nach dem Login kopieren

现在匹配到的长度是 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>
Nach dem Login kopieren

此时匹配到的长度为 3, 查找到 table[partial_match_length-1] 即 index 为 2 对应的值为 1,所以可向前移动的距离为 

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>
Nach dem Login kopieren

此时我们需要查找的字符串其长度已经超出剩余可用来匹配的字符串了,所以可直接结束匹配,得到结论:没有查找到结果。

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&#39;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>
Nach dem Login kopieren

时间复杂度

因为算法中涉及两部分字符串的线性对比,其时间复杂度为两字符串长度之和,假设需要搜索的关键词长度为 k,总字符串长度为 m,则时间复杂度为 O(k+m)。

Das obige ist der detaillierte Inhalt vonDamit Sie den KMP-Algorithmus leicht verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:cnblogs.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage