Das Prinzip des KMP-Algorithmus, sprechen Sie über das Verständnis von „j = next[j]“

Warum diesen Artikel schreiben?

  Kürzlich lerne ich die Datenstruktur. Ich habe in den letzten zwei Tagen gerade den KMP-Algorithmus gelernt . Ich habe ein gutes Verständnis der Logik des KMP-Algorithmus, aber ich stecke im Codeteil fest. Tatsächlich stecke ich hauptsächlich fest    die Aussage j=next[j]. Beim Schreiben dieses Artikels geht es zum einen darum, meine eigenen Lernergebnisse aufzuzeichnen, und zum anderen darum, Bedürftigen zu helfen, denn ich habe viele Artikel und Videos gefunden, aber im Grunde habe ich es hier nicht klar erklärt, sondern nur hier hinzugefügt Lassen Sie es leer stehen (halte es persönlich für leer).

Was ist der KMP-Algorithmus?

  Der KMP-Algorithmus (Knuth-Morris-Pratt-Algorithmus) ist ein bekannter String-Matching-Algorithmus mit hoher Effizienz. Da der Algorithmus von DEKnuth, JHMorris und VRPratt gemeinsam vorgeschlagen wurde, wird er als KMP-Algorithmus bezeichnet.

 Die Kernidee des KMP-Algorithmus

  Der KMP-Algorithmus ist eigentlich das Problem, die längste gemeinsame Präfix-Teilzeichenfolge zu finden . Was bedeutet das? Schauen Sie sich bitte das Bild unten an:
Fügen Sie hier eine Bildbeschreibung ein
  Hier zeigen der Zeiger auf die Hauptzeichenfolge und der Zeiger auf die Musterzeichenfolge auf [E] und [F ] bzw. (Der rote Teil im Bild) Wie berechnet man also die längste gemeinsame Präfix-Teilzeichenfolge ? Dies ist vor allem im grünen Teil des Bildes oben zu sehen.
Fügen Sie hier eine Bildbeschreibung ein
  Schauen Sie sich das Bild oben an. Die Daten in den beiden roten Kästchen im Bild oben sind gleich (beide sind ABC), und im Bild kann keine längere Datenzeichenfolge gefunden werden, um die Anforderungen zu erfüllen. Dies ist das längste gemeinsame Präfix Teilzeichenfolge . Dann:
Fügen Sie hier eine Bildbeschreibung ein
  Das obige Bild ist die Kernidee des KMP-Algorithmus, da dieser Artikel hauptsächlich über das nächste Array spricht . Die Idee von KMP ist also ungefähr hier. Wenn Sie eine detailliertere Erklärung benötigen, können Sie andere Blogger finden Artikel.

Berauschendes nächstes Array

  Das Bild unten zeigt den Code von Yan Weimin (C-Sprachversion) zum Generieren des nächsten Arrays.
Fügen Sie hier eine Bildbeschreibung ein
  Tatsächlich ist der vordere Teil dieses Codes relativ einfach zu verstehen, hauptsächlich weil Zweifel an der fünften Codezeile bestehen.
Fügen Sie hier eine Bildbeschreibung ein
  Dann lassen Sie uns darüber sprechen j=nächste[j] Frage

Sprechen Sie über j=next[j]

  Die „gemeinsame Teilzeichenfolge mit dem längsten Präfix“ hat nichts mit der Hauptzeichenfolge zu tun, sondern ist vollständig ein Attribut der Musterzeichenfolge.Ich glaube, jeder hätte diesen Punkt verstehen sollen, aber die Verwendung von Code zum Finden des nächsten Arrays ist zu einem Problem geworden.
Fügen Sie hier eine Bildbeschreibung ein
  Sehen Sie sich dieses Bild weiter an. Lassen Sie uns zunächst über den Code mit der Bezeichnung 2 sprechen. Sie können diese Codezeile als das folgende Bild verstehen. Initialisieren Sie die beiden Zeiger i und j so, dass sie jeweils auf die Position im Bild zeigen, und setzen Sie next [ 1 ] direkte Zuweisung 0
Fügen Sie hier eine Bildbeschreibung ein

  Ich weiß nicht, ob Sie etwas verwirrt sind, denn in der obigen Einleitung vergleichen wir den grünen Teil (K Zeichen) vor dem Zeiger, der nicht die Zeichen enthält, auf die der Zeiger zeigt, wie in der Abbildung gezeigt Unten, aber der Code mit der Bezeichnung 4 vergleicht
Fügen Sie hier eine Bildbeschreibung ein
  Zeiger direkt. Der Wert des Elements, auf das i und j zeigen ? Tatsächlich gibt es hier nichts Falsches. Jeder sieht, dass zuerst der Wert verglichen wird, dann der Zeiger um eins erhöht wird und schließlich der Wert des nächsten angegeben wird. Einfach ausgedrückt geht es darum, zuerst zu vergleichen und dann den Zeiger zurück zu bewegen und dem nächsten Array einen Wert zuzuweisen, nachdem es fertig ist. Ich weiß nicht, ob das jeder versteht, aber tatsächlich ist es so. Die hier verwendete Logik ist folgende :
Fügen Sie hier eine Bildbeschreibung ein

Um das nächste Array   zu finden, müssen Sie die größte gemeinsame Präfix-Teilzeichenfolge finden, wie in der Abbildung oben gezeigt. Wenn das nächste [i], das einem Element (D) in der Musterzeichenfolge entspricht, nur j ist, ist dies die Position des j-Zeigers in der Abbildung oben [tatsächlich gibt es hier nur j -1 Zeichen ist eine Übereinstimmung (wie die beiden grünen Teile im Bild oben kann es Null sein), verwechseln Sie es nicht! ], dann besteht der nächste Schritt darin, das Problem von i+1 zu lösen. Derzeit gibt es zwei Fälle:
Fall 1: Das neue Zeichen D ist dasselbe wie das j-te Zeichen s[j] , wie in der folgenden Abbildung dargestellt:
Fügen Sie hier eine Bildbeschreibung ein
  In diesem Fall ist next[i+1] = next[i] + 1, das ist relativ leicht zu verstehen und ich glaube, dass es jedem auf einen Blick einfallen kann.

Fall 2: Das neue Zeichen D unterscheidet sich vom j-ten Zeichen s[j] , wie in der folgenden Abbildung dargestellt:
Fügen Sie hier eine Bildbeschreibung ein

  Viele Leute stecken bei diesem Schritt fest, wenn sie den KMP-Algorithmus lesen. Ich glaube, dass viele Leute, die diesen Artikel heute lesen, auch hier sind. Die berüchtigte Rollback-Operation - j = next[j] erscheint hier.
  Zu diesem Zeitpunkt kann der ursprüngliche grüne Teil nicht mehr verwendet werden. Um die Anforderung der längsten gemeinsamen Präfix-Teilzeichenfolge zu erfüllen, müssen wir den grünen Teil wie folgt verkleinern:
Fügen Sie hier eine Bildbeschreibung ein

  Das „?“ im blauen Raster bedeutet, dass wir derzeit nicht wissen, um welche Zeichen es sich dabei handelt. Die beiden in orangefarbenen geschweiften Klammern eingeschlossenen Teile (der Teil, der in der Abbildung oben als neu markiert ist) sind die Teile, die wir neu kontrastieren müssen.
  Warum suchst du so danach? Unter der Annahme im vorherigen Artikel, dass die beiden Zeichen s[j] und s[i] unterschiedlich sind, schlägt der Abgleich fehl. Zu diesem Zeitpunkt müssen wir die längste gemeinsame Präfix-Teilzeichenfolge erneut finden. Müssen wir sie von Anfang an finden? Unmöglich, absolut unmöglich! Und das ist nicht nötig. Wenn es einen Unterschied zwischen der Suche von Grund auf und dem BF-Algorithmus gibt, erhöht sich die zeitliche Komplexität, was sehr unwirtschaftlich ist. Genossen, vergessen Sie nicht, wir haben immer noch viele verdeckte Karten in der Hand, also warum nicht Angst haben, es einfach tun!
  Schauen wir uns nun die Karten in unserer Hand an. Der Gegner fordert uns auf, next[i+1] bereitzustellen, da die beiden Zeichen s[j] und s[i] nicht übereinstimmen und wir dem nicht folgen können herkömmliche Routine des ersten Falles. , aber ein ausgehungertes Kamel ist größer als ein Pferd, und ich halte immer noch eine Reihe von Karten wie netx[i] in meiner Hand. Was ist next [ i
Fügen Sie hier eine Bildbeschreibung ein
  ] , d das i-te Zeichen (nein Enthält das i-te Zeichen) Die Zeichenfolge, die durch Vorwärtszählen von j-1 Zeichen erhalten wird, ist genau dieselbe wie die Teilzeichenfolge, die durch Rückwärtszählen von j-1 Zeichen vom Anfang der Musterzeichenfolge erhalten wird. Die beiden Zeichenfolgen sind gleich, also das Grün im obigen Abbildungsteil. Der Unterschied zwischen den Zeichen in s[j] und s[i] führt zu einer erneuten Übereinstimmung. Die Idee besteht darin, die längstmögliche Teilzeichenfolge in den Zeichen in der Nähe von s[i] auszuwählen, um die Bedingungen des KMP-Algorithmus zu erfüllen.
  Hier müssen wir zunächst verstehen, warum der zugeschnittene grüne Teil nur kürzer als der ursprüngliche ist und es unmöglich ist, den ursprünglichen grünen Teil zu überschreiten. Hier ist eine kurze Erklärung. Wenn Sie das Gefühl haben, dass Sie es vollständig verstanden haben, können Sie es überspringen An diesem Punkt blicken wir weiter nach unten. Das Bild unten zeigt den längsten gemeinsamen Präfix-Teilstring (grüner Teil), den wir überprüft haben
Fügen Sie hier eine Bildbeschreibung ein
  Wenn wir auf die zweite Situation stoßen, das heißt, die Zeichen in s[j] und s[i] stimmen nicht überein, dann gehen wir davon aus, dass die Länge der erweiterten längsten gemeinsamen Präfix-Teilzeichenfolge die Anforderungen erfüllen kann (vorausgesetzt, die Erweiterung beträgt m Zeichen). erfüllt gerade die Anforderungen ), wie im Bild unten gezeigt:
Fügen Sie hier eine Bildbeschreibung ein
  Schauen Sie sich das Bild unten an, der längste gemeinsame Präfix-Teilstring (grüner Teil), der gerade überprüft wurde, wurde erweitert, was im Gegensatz zu dem steht, was bekannt ist, also wissen wir das Die längste gemeinsame Präfix-Teilzeichenfolge ist unmöglich, wurde erweitert.
Fügen Sie hier eine Bildbeschreibung ein
  Nun, es beweist, dass die längste gemeinsame Präfix-Teilzeichenfolge nicht verlängert werden kann, ohne unverändert zu bleiben, sondern nur verkürzt werden kann. Inwieweit muss die Verkürzung gekürzt werden bzw. um wie viel soll am Stück gekürzt werden? Dies bezieht sich auf das nächste Array. Erhalten Sie weiterhin dieses Bild:
Fügen Sie hier eine Bildbeschreibung ein

  Das Wichtigste, was Sie gesehen haben müssen, ist der grüne Teil im Neuzustand . Und der grüne Teil ist die Karte in unserer Hand, Brüder, es ist der längste gemeinsame Präfix-Teilstring von s[i] .
  Schauen Sie sich das Bild unten an (dh den oberen Teil des Bildes oben). Tatsächlich besteht unsere Aufgabe darin, das längste gemeinsame Präfix von s [i + 1] in den Teilzeichenfolgen Area1 und Area2 zu finden.
Fügen Sie hier eine Bildbeschreibung ein
  Zufällig sind die Zeichenfolgen in diesen beiden Bereichen genau gleich, sodass wir nur einen davon untersuchen müssen. Dann werden wir Area1 untersuchen und Area1 herausnehmen.
Fügen Sie hier eine Bildbeschreibung ein
  Sie sehen, der Zeiger j zeigt zu diesem Zeitpunkt immer noch auf den violetten Teil in der Abbildung. Und next[j] besteht darin, die längste gemeinsame Präfix-Teilzeichenfolge von s[j] zu finden! Das heißt:
Fügen Sie hier eine Bildbeschreibung ein
  Aber das Problem entsteht, weil wir die längste gemeinsame Präfix-Teilzeichenfolge von s[i+1] wollen, also müssen wir sicherstellen, dass die Elemente vor dem Zeichen s[i+1] genau mit den Elementen am Anfang übereinstimmen , Konfrontiert mit dem folgenden Problem, siehe Bild:
Fügen Sie hier eine Bildbeschreibung ein

  Ja, das heißt, wir können nur garantieren, dass der grüne Teil im Bild derselbe ist, aber wir können nicht garantieren, dass das blaue Quadrat mit einem Fragezeichen mit s[i] übereinstimmt. Wenn Sie diesen Satz nicht verstehen, verwenden Sie den folgenden Bild Vielleicht kann es Ihnen weiterhelfen:
Fügen Sie hier eine Bildbeschreibung ein
  Das Obige ist die Funktion von j = next [j]. Nachdem wir die Position verschoben haben, auf die der Zeiger j zeigt, müssen wir weiter vergleichen, ob die Zeichen in s [i] und s [j] sind die gleichen, aber das ist ein anderer Zyklus des Programms. Das Gleiche gilt für Fall 1, und der Unterschied besteht für Fall 2. Versuchen Sie es, bis der Erfolg gelingt oder die Länge des gemeinsamen Präfixes 0 ist.

Python-Code

  Das Schreiben eines Artikels ist wirklich ermüdend. Als nächstes werde ich für jeden einen Teil des Python-Codes anhängen. Der Code dient nur dazu, das nächste Array zu finden, daher ist er zwangsläufig nicht standardisiert. Wenn Sie ihn benötigen, können Sie ihn selbst nehmen.

# KMP 算法求next[i]的值
nex = [0]
ch = ("ABCABDABCABCM")  # 要求的模式串
i = 1
nex.insert(1, 0)
j = 0
q = [1]
u = [1]
while i < len(ch):
    if j == 0 or ch[i-1] == ch[j-1]:
        i += 1
        j += 1
        q = ch[i - 1]
        nex.insert(i, j)
    else:
        j = nex[j]
    u = ch[j - 1]
print(nex[1:])

Acho que você gosta

Origin blog.csdn.net/weixin_45911959/article/details/123468409
Recomendado
Clasificación