Wie bereits erwähnt, ähnelt dieses Problem dem allgemein bekannten Problem der Bearbeitungsentfernung (das der Levenshtein-Entfernung zugrunde liegt ). Es hat auch Gemeinsamkeiten mit beispielsweise der dynamischen Zeitverzerrungsentfernung (das Duplizieren oder „Stottern“ in Ihrer letzten Anforderung).
Schritte zur dynamischen Programmierung
x=x1…xny=y1…ymd(x,y)
min⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪d(x,y1…ym−1)+1d(x,y2…ym)+1d(x,y1…ym/2)+1d(x1…xn/2,y)+1d(x1…xn,y)+1d(x1…xn−1,y1…ym−1)if y=y1…ym/2y1…ym/2if x=x1…xn/2x1…xn/2if yn=ym▻ Add letter at end▻ Add letter at beginning▻ Doubling▻ Halving▻ Deletion▻ Ignoring last elt.
Hier besagt die letzte Option im Wesentlichen, dass die Konvertierung von FOOX in BARX der Konvertierung von FOO in BAR entspricht. Dies bedeutet, dass Sie die Option „Brief am Ende hinzufügen“ verwenden können, um den Stottern- (Duplizierungs-) Effekt und das Löschen an einem bestimmten Punkt zu erzielen. Das Problem ist, dass Sie automatisch auch ein beliebiges Zeichen in die Mitte der Zeichenfolge einfügen können , was Sie wahrscheinlich nicht möchten. (Dieses „Ignorieren identischer letzter Elemente“ ist die Standardmethode zum Löschen und Stottern an beliebigen Positionen. Es macht das Verbot beliebiger Einfügungen und das Hinzufügen von Hinzufügungen an beiden Enden jedoch etwas schwierig…)
Ich habe diese Aufschlüsselung aufgenommen, obwohl sie den Job nicht vollständig erledigt, falls jemand anderes sie irgendwie "retten" kann - und weil ich sie in meiner heuristischen Lösung unten verwende.
(Wenn Sie eine Aufschlüsselung wie diese erhalten könnten, die tatsächlich Ihre Entfernung definiert, müssten Sie nur Memoisierung hinzufügen, und Sie hätten eine Lösung. Da Sie jedoch nicht nur mit Präfixen arbeiten, tue ich das nicht. Ich glaube nicht, dass Sie nur Indizes für Ihre Memoisierung verwenden könnten. Möglicherweise müssen Sie die tatsächlichen, geänderten Zeichenfolgen für jeden Aufruf speichern. Dies würde sehr groß werden, wenn Ihre Zeichenfolgen eine beträchtliche Größe haben.)
Schritte zu einer heuristischen Lösung
Ein anderer Ansatz, der möglicherweise einfacher zu verstehen ist und viel weniger Platz benötigt, besteht darin, mit dem Algorithmus (im Grunde genommen am besten) nach dem kürzesten „Bearbeitungspfad“ von Ihrer ersten Zeichenfolge zu Ihrer zweiten zu suchen. erste Verzweigung). Der Suchraum wird direkt durch Ihre Bearbeitungsvorgänge definiert. Nun, für eine große Saite würden SieA∗A ∗Erhalten Sie eine große Nachbarschaft, da Sie ein beliebiges Zeichen löschen können (indem Sie für jede mögliche Löschung einen Nachbarn angeben) oder ein beliebiges Zeichen duplizieren können (wiederum eine lineare Anzahl von Nachbarn) und an beiden Enden ein beliebiges Zeichen hinzufügen können Geben Sie eine Anzahl von Nachbarn an, die der doppelten Alphabetgröße entspricht. (Hoffe nur, dass du nicht den vollen Unicode verwendest ;-) Mit einem so großen Fanout kannst du mit einem bidirektionalen oder einem VerwandtenA∗ eine beträchtliche Beschleunigung erzielen .
Damit funktioniert, benötigen Sie eine Untergrenze für die verbleibende Entfernung zu Ihrem Ziel. Ich bin mir nicht sicher, ob es hier eine offensichtliche Wahl gibt, aber Sie könnten eine dynamische Programmierlösung implementieren, die auf der oben angegebenen rekursiven Zerlegung basiert (wiederum mit möglichen Platzproblemen, wenn Ihre Zeichenfolgen sehr lang sind). Während diese Zerlegung Ihre Entfernung nicht genau berechnet, ist sie garantiert eine Untergrenze (weil sie freizügiger ist), was bedeutet, dass sie in als Heuristik funktioniert . (Wie eng es sein wird, weiß ich nicht, aber es wäre richtig.) Natürlich könnte die Memoisierung Ihrer gebundenen Funktion über alle Berechnungen der gebundenen während IhresA ∗ A ∗A∗A∗A∗Lauf. (Ein Zeit- / Raum-Kompromiss dort.)
So…
Die Effizienz meiner vorgeschlagenen Lösung scheint ziemlich stark von (1) der Länge Ihrer Zeichenfolgen und (2) der Größe Ihres Alphabets abzuhängen. Wenn beides nicht riesig ist, könnte es funktionieren. Das ist:
- Implementieren Sie die Untergrenze für Ihre Entfernung mithilfe meiner rekursiven Zerlegung und dynamischen Programmierung (z. B. mithilfe einer gespeicherten rekursiven Funktion).
- Implementieren Sie (oder bidirektionales ) mit Ihren Bearbeitungsoperationen als "Verschiebungen" im Statusraum und der auf dynamischer Programmierung basierenden Untergrenze.A ∗A∗A∗
Ich kann nicht wirklich garantieren, wie effizient es sein würde, aber es sollte korrekt sein, und es wäre wahrscheinlich viel besser als eine Brute-Force-Lösung.
Wenn nichts anderes, hoffe ich, dass dies Ihnen einige Ideen für weitere Untersuchungen gibt.