Ich bin seit einiger Zeit festgefahren, was der schnellste Algorithmus für die Suche nach Zeichenfolgen ist, habe viele Meinungen gehört, bin mir aber am Ende nicht sicher.
Ich habe einige Leute sagen hören, dass der schnellste Algorithmus Boyer-Moore ist und einige sagen, dass Knuth-Morris-Pratt tatsächlich schneller ist.
Ich habe nach der Komplexität bei beiden gesucht, aber sie sehen größtenteils gleich aus O(n+m)
. Ich habe festgestellt, dass Boyer-Moore im schlimmsten Fall eine O(nm)
Komplexität im Vergleich zu Knuth-Morris-Pratt hat, die O (m + 2 * n) hat. Wobei n = Länge des Textes und m = Länge des Musters.
Soweit ich weiß, hat Boyer-Moore eine linear schlechteste Zeit, wenn ich die Galil-Regel anwenden würde.
Meine Frage: Über alles, was eigentlich der schnellste String-Suchalgorithmus ist (Diese Frage beinhaltet alle möglichen Stichalgorithmen, nicht nur Boyer-Moore und Knuth-Morris-Pratt).
Edit: Aufgrund dieser Antwort
Was ich genau suche ist:
Angesichts eines Textes T
und eines Musters muss P
ich alle Auftritte von P
in finden T
.
Auch die Länge von P und T ist von [1,2 000 000]
und das Programm muss unter 0,15 Sekunden laufen.
Ich weiß, dass KMP und Rabin-Karp ausreichen, um das Problem zu 100% zu lösen, aber ich wollte Boyer-Moore unbedingt implementieren. Welches wäre das Beste für diese Art der Mustersuche?
quelle
Antworten:
Dies hängt von der Art der Suche ab, die Sie durchführen möchten. Jeder der Algorithmen ist für bestimmte Suchtypen besonders leistungsfähig, aber Sie haben den Kontext Ihrer Suchvorgänge nicht angegeben.
Hier sind einige typische Gedanken zu Suchtypen:
Boyer-Moore: Analysiert das Muster vor und vergleicht es von rechts nach links. Wenn eine Nichtübereinstimmung auftritt, wird die anfängliche Analyse verwendet, um zu bestimmen, wie weit das Muster in Bezug auf den gesuchten Text verschoben werden kann. Dies funktioniert besonders gut bei langen Suchmustern. Insbesondere kann es sublinear sein, da Sie nicht jedes einzelne Zeichen Ihres Textes lesen müssen.
Knuth-Morris-Pratt: analysiert das Muster ebenfalls vorab, versucht jedoch, alles, was bereits im ersten Teil des Musters vorhanden war, wiederzuverwenden, um zu vermeiden, dass das Muster erneut abgeglichen werden muss. Dies kann recht gut funktionieren, wenn Ihr Alphabet klein ist (z. B. DNA-Basen), da Sie eine höhere Wahrscheinlichkeit haben, dass Ihre Suchmuster wiederverwendbare Submuster enthalten.
Aho-Corasick: Benötigt viel Vorverarbeitung, tut dies jedoch für eine Reihe von Mustern. Wenn Sie wissen, dass Sie immer wieder nach denselben Suchmustern suchen, ist dies viel besser als das andere, da Sie Muster nur einmal und nicht einmal pro Suche analysieren müssen.
Daher gibt es, wie in CS üblich, keine eindeutige Antwort auf die Gesamtbeste . Es geht vielmehr darum, das richtige Werkzeug für den jeweiligen Job auszuwählen.
Ein weiterer Hinweis zu Ihrer Worst-Case-Argumentation: Überlegen Sie sich, welche Suchvorgänge für diesen Worst-Case erforderlich sind, und ob diese für Ihren Fall wirklich relevant sind. Zum Beispiel
O(mn)
ergibt sich die schlimmste Komplexität des Boyer-Moore-Algorithmus aus einem Suchmuster und einem Text, der jeweils nur ein Zeichen enthält (wie das Findenaaa
inaaaaaaaaaaaaaaaaaaaaa
). Müssen Sie für solche Suchvorgänge wirklich schnell sein?quelle
Ich bin zwar etwas spät dran, um diese Frage zu beantworten, aber ich denke, ich bin
Z-Algorithm
viel schneller als alle seine Gegenstücke. Die Komplexität im ungünstigsten Fall ist O (m + n) und es ist keine Vorverarbeitung des Musters / Texts erforderlich. Im Vergleich zu den anderen Algorithmen ist es auch sehr einfach zu codieren.Es funktioniert auf folgende Weise.
Zum Beispiel gibt es eine Zeichenfolge
S ='abaaba'
. Wir sollenz(i)
Werte finden füri=0 to len(S)-1
. Bevor ich auf die Erklärung eingehe, möchte ich zunächst einige Definitionen festlegen.z(i)
= nein von Zeichen des PräfixesS
, das mit dem Präfix von übereinstimmts(i)
.s(i)
=ith
Suffix vonS
.Das Folgende sind die
s(i)
Werte fürs = 'abaaba'
.Die z-Werte sind jeweils
Weitere Informationen zum Algorithmus finden Sie unter den folgenden Links.
http://codeforces.com/blog/entry/3107
https://www.youtube.com/watch?v=MFK0WYeVEag
Jetzt ist O (N) erforderlich, um alle
z
Werte ohne zusätzlichen Aufwand für die Vorverarbeitung zu ermitteln. Man würde sich jetzt fragen, wie man diese Logik verwenden kann, um Muster in einer gegebenen Zeichenkette abzugleichen.Schauen wir uns ein Beispiel an. Muster (P):
aba
, Text (T):aacbabcabaad
.Geben Sie dies in das Formular P $ T ein. (
$
- Jedes Zeichen, das weder in einem Muster noch in einem Text vorkommt. Ich werde in Kürze auf die Bedeutung von$
eingehen.)P$T
=aba$aacbabcabaad
Wir wissen
len(P)
= 3.Alle z-Werte von
P$T
sindNun welche
z(i)
=len(P)
.Ans = 11.
Unser Muster ist also beiAns-len(P)-1
= vorhanden7
.-1
ist für$
Charakter.Jetzt
$
ist es wichtig, warum oder welche Sonderzeichen es gibt. BetrachtenP = 'aaa'
undT = 'aaaaaaa'
. Ohne das Sonderzeichen haben allez(i)
inkrementelle Werte. Mit den folgenden Formeln kann man die Position des Musters im Text immer noch finden:Zustand:
z(i)
> =len(P)
und Position:Ans-len(P)
. In diesem Fall wird die Situation jedoch etwas knifflig und verwirrend. Ich persönlich bevorzuge die Sonderzeichentechnik.quelle
z
ist eine Vorverarbeitung. Es ist jedoch eine gute Erklärung.O(n)
Aufgrund dieser Antwort habe ich einen Weg gefunden, von der KMP-Vorverarbeitung zur Z-Vorverarbeitung zu konvertieren. HierVerwenden Sie inhaltsadressierbaren Speicher , der in der Software in Form einer virtuellen Adressierung implementiert ist (Zeigen von Buchstaben auf Buchstaben).
Es ist ein bisschen überflüssig für einen durchschnittlichen String-Matching-Algorithmus.
CAM kann mit einer großen Anzahl von Mustern gleichzeitig übereinstimmen, bis zu 128-Buchstaben-Mustern (wenn es sich um ASCII handelt; wenn es sich nur um Unicode handelt, 64). Und es ist ein Aufruf pro Buchstabenlänge in der Zeichenfolge, mit der Sie übereinstimmen möchten, und ein zufälliger Lesevorgang aus dem Speicher pro Länge der maximalen Musterlänge. Wenn Sie also eine 100.000-Buchstaben-Zeichenfolge mit bis zu 90.000.000 Mustern gleichzeitig analysieren (was etwa 128 GiB zum Speichern einer so großen Anzahl von Mustern benötigt), sind 12.800.000 zufällige Lesevorgänge aus dem RAM erforderlich, was in 1 ms der Fall ist.
So funktioniert die virtuelle Adressierung.
Wenn ich mit 256 Startadressen beginne, die den ersten Buchstaben darstellen, zeigen diese Buchstaben auf 256 der nächsten Buchstaben. Wenn ein Muster nicht vorhanden ist, speichern Sie es nicht.
Wenn ich also weiterhin Buchstaben mit Buchstaben verbinde, ist das so, als ob 128 virtuelle Adressierungsbereiche auf virtuelle Adressierung verweisen würden.
Das wird funktionieren - aber um 900.000.000 Muster gleichzeitig abzugleichen, muss noch ein letzter Trick hinzugefügt werden - und es wird die Tatsache ausgenutzt, dass Sie anfangen, diese Buchstabenpuffer häufig wiederzuverwenden, aber später verstreut es sich. Wenn Sie den Inhalt auflisten, anstatt alle 256 Zeichen zuzuweisen, verlangsamt er sich nur geringfügig, und Sie erhalten eine 100-fache Kapazitätserhöhung, da im Grunde genommen nur 1 Buchstabe in jedem Buchstabenzeigerpuffer verwendet wird (den ich als "" bezeichnet habe). Flucht').
Wenn Sie eine Übereinstimmung mit der Zeichenfolge "Nächster Nachbar" erzielen möchten, werden viele davon parallel ausgeführt und in einer Hierarchie gesammelt, sodass Sie Ihren Fehler unbefangen weitergeben. Wenn Sie versuchen, mit nur einem Nachbarn den nächsten Nachbarn zu finden, sind Sie voreingenommen gegenüber dem Anfang des Baums.
quelle