Einem Freund von mir wurde heute beim Interview die folgende Frage für die Position des Softwareentwicklers gestellt:
Bei zwei Zeichenfolgen s1
und s2
wie werden Sie überprüfen, ob s1
es sich um eine gedrehte Version von handelt s2
?
Beispiel:
Wenn s1 = "stackoverflow"
dann sind die folgenden einige seiner gedrehten Versionen:
"tackoverflows"
"ackoverflowst"
"overflowstack"
wo wie "stackoverflwo"
ist keine gedrehte Version.
Die Antwort, die er gab, war:
Nehmen Sie
s2
und finden Sie das längste Präfix, das eine Unterzeichenfolge vons1
ist, die Ihnen den Drehpunkt gibt. Wenn Sie diesen Punkt gefunden haben, brechen Sies2
an diesem Punkt ab, um zu erhalten,s2a
unds2b
überprüfen Sie dann einfach, obconcatenate(s2a,s2b) == s1
Es sieht nach einer guten Lösung für mich und meinen Freund aus. Aber der Interviewer dachte anders. Er bat um eine einfachere Lösung. Bitte helfen Sie mir, indem Sie mir sagen, wie Sie das machen würden Java/C/C++
.
Danke im Voraus.
Antworten:
Zunächst stellen Sie sicher ,
s1
unds2
sind von gleicher Länge. Überprüfen Sie dann, obs2
ein Teilstrings1
verkettet ist mits1
:In Java:
quelle
(s1+s1).contains(s2)
in Java verwenden.s1+s1
. Klar, die alle Teilzeichen mit Größes1.length
sind Drehungens1
, durch Konstruktion. Daher muss jede Zeichenfolge mits1.length
einer Teilzeichenfolges1+s1
eine Drehung von seins1
.Eine bessere Antwort wäre sicherlich: "Nun, ich würde die Stackoverflow-Community fragen und wahrscheinlich innerhalb von 5 Minuten mindestens 4 wirklich gute Antworten haben." Gehirne sind gut und alle, aber ich würde einen höheren Wert auf jemanden legen, der weiß, wie man mit anderen zusammenarbeitet, um eine Lösung zu finden.
quelle
Ein weiteres Python-Beispiel (basierend auf DER Antwort):
quelle
s2
eher an Duplikate als an Duplikates1
... dann wurde mir klar, dass die Beziehung sowieso symmetrisch war.in
Operator keinen O (n) -Algorithmus?s1 in s2
optimiert ist. Eine Beschreibung des Algorithmus finden Sie unter effbot.org/zone/stringlib.htm . Google scheint darauf hinzuweisen, dass Java keine schnelle Zeichenfolgensuche hat (siehe zum Beispiel johannburkard.de/software/stringsearch ), obwohl ich bezweifle, dass es irgendetwas kaputt machen würde, wenn sie es ändern würden.Da andere eine quadratische Worst-Case-Zeitkomplexitätslösung eingereicht haben, würde ich eine lineare hinzufügen (basierend auf dem KMP-Algorithmus ):
Arbeitsbeispiel
quelle
EDIT: Die akzeptierte Antwort ist deutlich eleganter und effizienter als diese, wenn Sie sie erkennen. Ich habe diese Antwort als das belassen, was ich tun würde, wenn ich nicht daran gedacht hätte, die ursprüngliche Saite zu verdoppeln.
Ich würde es nur brutal erzwingen. Überprüfen Sie zuerst die Länge und versuchen Sie dann jeden möglichen Rotationsversatz. Wenn keiner von ihnen funktioniert, geben Sie false zurück. Wenn einer von ihnen dies tut, geben Sie sofort true zurück.
Es besteht keine besondere Notwendigkeit zu verketten - verwenden Sie einfach Zeiger (C) oder Indizes (Java) und gehen Sie beide entlang, einen in jeder Zeichenfolge - beginnend am Anfang einer Zeichenfolge und dem aktuellen Rotationsversatz der Kandidaten in der zweiten Zeichenfolge und bei Bedarf umbrechen . Überprüfen Sie die Zeichengleichheit an jedem Punkt in der Zeichenfolge. Wenn Sie am Ende der ersten Zeichenfolge angelangt sind, sind Sie fertig.
Es wäre wahrscheinlich genauso einfach zu verketten - obwohl es wahrscheinlich weniger effizient ist, zumindest in Java.
quelle
Hier ist eine, die Regex nur zum Spaß verwendet:
Sie können es etwas einfacher machen, wenn Sie ein spezielles Trennzeichen verwenden, das garantiert nicht in einer der beiden Zeichenfolgen enthalten ist.
Sie können stattdessen auch Lookbehind mit endlicher Wiederholung verwenden:
quelle
Whoa, whoa ... warum sind alle so begeistert von einer
O(n^2)
Antwort? Ich bin mir sicher, dass wir es hier besser machen können. Die obige Antwort enthält eineO(n)
Operation in einerO(n)
Schleife (den Aufruf von substring / indexOf). Auch mit einem effizienteren Suchalgorithmus; sagenBoyer-Moore
oderKMP
, der schlimmste Fall ist immer nochO(n^2)
mit Duplikaten.Eine
O(n)
zufällige Antwort ist einfach; Nehmen Sie einen Hash (wie einen Rabin-Fingerabdruck), der einO(1)
Schiebefenster unterstützt . Hash-String 1, dann Hash-String 2, und verschieben Sie das Fenster für Hash 1 um den String und prüfen Sie, ob die Hash-Funktionen kollidieren.Wenn wir uns vorstellen, dass der schlimmste Fall so etwas wie "Scannen von zwei DNA-Strängen" ist, steigt die Wahrscheinlichkeit von Kollisionen, und dies degeneriert wahrscheinlich zu so etwas
O(n^(1+e))
oder so (hier nur erraten).Schließlich gibt es eine deterministische
O(nlogn)
Lösung, die außen eine sehr große Konstante hat. Grundsätzlich besteht die Idee darin, eine Faltung der beiden Saiten vorzunehmen. Der Maximalwert der Faltung ist die Rotationsdifferenz (wenn sie gedreht werden); einO(n)
Scheck bestätigt. Das Schöne ist, dass wenn es zwei gleiche Maximalwerte gibt, beide auch gültige Lösungen sind. Sie können die Faltung mit zwei FFTs und einem Punktprodukt sowie einer iFFT durchführennlogn + nlogn + n + nlogn + n == O(nlogn)
.Da Sie nicht mit Nullen auffüllen können und nicht garantieren können, dass die Zeichenfolgen 2 ^ n lang sind, sind die FFTs nicht die schnellen. Sie werden die langsamen sein,
O(nlogn)
aber immer noch eine viel größere Konstante als der CT-Algorithmus.Alles in allem bin ich absolut zu 100% sicher, dass es hier eine deterministische
O(n)
Lösung gibt, aber verdammt, wenn ich sie finden kann.quelle
%stringsize
) ist garantiert eine lineare Zeit.Stellen Sie zunächst sicher, dass die beiden Saiten die gleiche Länge haben. In C können Sie dies dann mit einer einfachen Zeigeriteration tun.
quelle
Hier ist ein
O(n)
und an Ort und Stelle Alghoritmus. Es verwendet den<
Operator für die Elemente der Zeichenfolgen. Es ist natürlich nicht meins. Ich habe es von hier genommen (Die Seite ist in polnischer Sprache. Ich bin in der Vergangenheit einmal darauf gestoßen und konnte so etwas jetzt nicht auf Englisch finden, also zeige ich, was ich habe :)).quelle
Ich denke, es ist besser, dies zu tun in
Java
:In Perl würde ich tun:
oder noch besser mit der Indexfunktion anstelle des regulären Ausdrucks:
quelle
\Q
in/\Q$string2/
.\Q
zitiert Sonderzeichen in$string2
. Ohne sie.
würde eine Drehung einer 1-stelligen Zeichenfolge betrachtet.Ich bin mir nicht sicher, ob dies die effizienteste Methode ist, aber es könnte relativ interessant sein : die Burrows-Wheeler-Transformation . Gemäß dem WP-Artikel ergeben alle Umdrehungen des Eingangs den gleichen Ausgang. Für Anwendungen wie die Komprimierung ist dies nicht wünschenswert, daher wird die ursprüngliche Drehung angezeigt (z. B. durch einen Index; siehe Artikel). Für einen einfachen rotationsunabhängigen Vergleich klingt dies jedoch ideal. Natürlich ist es nicht unbedingt ideal effizient!
quelle
Nehmen Sie jedes Zeichen als Amplitude und führen Sie eine diskrete Fourier-Transformation durch. Wenn sie sich nur durch Drehung unterscheiden, sind die Frequenzspektren innerhalb des Rundungsfehlers gleich. Dies ist natürlich ineffizient, es sei denn, die Länge ist eine Potenz von 2, so dass Sie eine FFT durchführen können :-)
quelle
Bisher hat noch niemand einen Modulo-Ansatz angeboten. Hier ist einer:
Ausgabe:
[EDIT: 2010-04-12]
piotr bemerkte den Fehler in meinem Code oben. Es tritt ein Fehler auf, wenn das erste Zeichen in der Zeichenfolge zweimal oder öfter vorkommt. Zum Beispiel ergab ein
stackoverflow
Test gegenowstackoverflow
false, wenn es wahr sein sollte.Vielen Dank an piotr für das Erkennen des Fehlers.
Hier ist der korrigierte Code:
Hier ist die Ausgabe:
Hier ist der Lambda-Ansatz:
Hier ist die Ausgabe des Lambda-Ansatzes:
quelle
Da hat niemand eine C ++ Lösung gegeben. hier ist es es:
quelle
Der einfache Zeigerrotationstrick von Opera funktioniert, ist jedoch im schlimmsten Fall zur Laufzeit äußerst ineffizient. Stellen Sie sich einfach eine Zeichenfolge mit vielen langen, sich wiederholenden Zeichenfolgen vor, dh:
Die "Schleife, bis eine Nichtübereinstimmung vorliegt, dann um eins erhöht und erneut versucht" ist rechnerisch ein schrecklicher Ansatz.
Um zu beweisen, dass Sie den Verkettungsansatz in einfachem C ohne großen Aufwand ausführen können, ist hier meine Lösung:
Dies ist in der Laufzeit linear, auf Kosten der O (n) -Speicherauslastung im Overhead.
(Beachten Sie, dass die Implementierung von strstr () plattformspezifisch ist, aber wenn sie besonders hirntot ist, kann sie immer durch eine schnellere Alternative wie den Boyer-Moore-Algorithmus ersetzt werden.)
quelle
strstr()
O (n + m)? Wenn der Standard (oder etwas anderes) Ihnen keine lineare Laufzeit von garantiertstrstr()
, können Sie nicht behaupten, dass der gesamte Algorithmus eine lineare Zeitkomplexität aufweist.s1SelfConcat
: Erst seit C9x erlaubt C variable Arraygrößen (obwohl GCC dies viel länger zugelassen hat), und Sie werden Probleme haben, große Zeichenfolgen auf dem Stapel zuzuweisen. Josef Kreinin schrieb einen sehr amüsanten Blog-Beitrag über dieses Problem. Außerdem ist Ihre Lösung mit Boyer-Moore immer noch quadratisch. Sie wollen KMP.C #:
quelle
Ich mag DIE Antwort, die prüft, ob s2 eine mit s1 verkettete Teilzeichenfolge von s1 ist.
Ich wollte eine Optimierung hinzufügen, die ihre Eleganz nicht verliert.
Anstatt die Zeichenfolgen zu verketten, können Sie eine Verknüpfungsansicht verwenden (ich kenne keine andere Sprache, aber für C ++ Boost.Range bieten Sie solche Ansichten an).
Da die Überprüfung, ob ein String ein Teilstring eines anderen ist, eine lineare durchschnittliche Komplexität aufweist (die Komplexität im schlimmsten Fall ist quadratisch), sollte diese Optimierung die Geschwindigkeit im Durchschnitt um den Faktor 2 verbessern.
quelle
Eine reine Java-Antwort (ohne Null-Checks)
quelle
Und jetzt etwas ganz anderes.
Wenn Sie eine wirklich schnelle Antwort in einem eingeschränkten Kontext wünschen, wenn sich die Zeichenfolgen nicht gegenseitig drehen
Einverstanden, es kann fehlschlagen, aber es ist sehr schnell zu sagen, ob Zeichenfolgen nicht übereinstimmen, und wenn sie übereinstimmen, können Sie immer noch einen anderen Algorithmus wie die Verkettung von Zeichenfolgen verwenden, um dies zu überprüfen.
quelle
Eine weitere Ruby-Lösung basierend auf der Antwort:
quelle
Es ist sehr einfach in der Verwendung von PHP zu schreiben
strlen
undstrpos
Funktionen:Ich weiß nicht, was
strpos
intern verwendet wird, aber wenn es KMP verwendet, ist dies zeitlich linear.quelle
Kehren Sie eine der Saiten um. Nehmen Sie die FFT von beiden (behandeln Sie sie als einfache Folgen von ganzen Zahlen). Multiplizieren Sie die Ergebnisse punktuell. Mit inverser FFT zurücktransformieren. Das Ergebnis hat einen einzelnen Peak, wenn die Saiten Rotationen voneinander sind. Die Position des Peaks gibt an, um wie viel sie relativ zueinander gedreht sind.
quelle
Warum nicht so etwas?
Natürlich können Sie auch Ihre eigene IndexOf () -Funktion schreiben. Ich bin mir nicht sicher, ob .NET einen naiven oder einen schnelleren Weg verwendet.
Naiv:
Schneller:
Bearbeiten: Ich könnte einige Probleme haben; Ich habe keine Lust zu überprüfen. ;)
quelle
Ich würde das in Perl machen :
quelle
quelle
Verbinden Sie sich
string1
mitstring2
und verwenden Sie den KMP-Algorithmus , um zu überprüfen, obstring2
eine neu gebildete Zeichenfolge vorhanden ist. Weil die zeitliche Komplexität von KMP geringer ist alssubstr
.quelle