Ich muss eine Funktion erstellen, die eine Zeichenfolge akzeptiert, und sie sollte zurückgegeben werden true
oder darauf false
basieren, ob die Eingabe aus einer wiederholten Zeichenfolge besteht. Die Länge der angegebenen Zeichenfolge ist immer größer als 1
und die Zeichenfolge muss mindestens eine Wiederholung haben.
"aa" // true(entirely contains two strings "a")
"aaa" //true(entirely contains three string "a")
"abcabcabc" //true(entirely containas three strings "abc")
"aba" //false(At least there should be two same substrings and nothing more)
"ababa" //false("ab" exists twice but "a" is extra so false)
Ich habe die folgende Funktion erstellt:
function check(str){
if(!(str.length && str.length - 1)) return false;
let temp = '';
for(let i = 0;i<=str.length/2;i++){
temp += str[i]
//console.log(str.replace(new RegExp(temp,"g"),''))
if(!str.replace(new RegExp(temp,"g"),'')) return true;
}
return false;
}
console.log(check('aa')) //true
console.log(check('aaa')) //true
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false
Dies zu überprüfen ist Teil des eigentlichen Problems. Ich kann mir eine solche nicht effiziente Lösung nicht leisten. Zuallererst durchläuft es die Hälfte der Saite.
Das zweite Problem ist, dass es replace()
in jeder Schleife verwendet wird, was es langsam macht. Gibt es eine bessere Lösung für die Leistung?
javascript
string
algorithm
Maheer Ali
quelle
quelle
Antworten:
Es gibt einen kleinen Satz über solche Saiten.
Hier bedeutet eine Drehung, dass einige Zeichen von der Vorderseite der Zeichenfolge gelöscht und nach hinten verschoben werden. Zum Beispiel
hello
könnte die Zeichenfolge gedreht werden, um eine dieser Zeichenfolgen zu bilden:Um zu sehen, warum dies funktioniert, nehmen wir zunächst an, dass eine Zeichenfolge aus k wiederholten Kopien einer Zeichenfolge w besteht. Wenn Sie dann die erste Kopie des wiederholten Musters (w) von der Vorderseite der Zeichenfolge löschen und auf die Rückseite heften, erhalten Sie dieselbe Zeichenfolge zurück. Die umgekehrte Richtung ist etwas schwieriger zu beweisen, aber die Idee ist, dass Sie, wenn Sie eine Zeichenfolge drehen und das zurückbekommen, womit Sie begonnen haben, diese Drehung wiederholt anwenden können, um die Zeichenfolge mit mehreren Kopien desselben Musters zu kacheln (wobei dieses Muster das ist Zeichenfolge, die Sie zum Ende bewegen mussten, um die Drehung durchzuführen).
Nun stellt sich die Frage, wie überprüft werden kann, ob dies der Fall ist. Dafür gibt es einen anderen schönen Satz, den wir verwenden können:
Als Beispiel können wir sehen, dass dies
lohel
eine Rotation vonhello
wie folgt ist:In unserem Fall wissen wir, dass jeder String x immer ein Teilstring von xx ist (er erscheint zweimal, einmal bei jeder Kopie von x). Im Grunde müssen wir nur überprüfen, ob unsere Zeichenfolge x eine Teilzeichenfolge von xx ist, ohne dass sie beim ersten oder halben Zeichen übereinstimmt. Hier ist ein Einzeiler dafür:
Angenommen, es
indexOf
wird ein schneller String-Matching-Algorithmus implementiert, der in der Zeit O (n) ausgeführt wird, wobei n die Länge des Eingabe-Strings ist.Hoffe das hilft!
quelle
Sie können dies durch eine Erfassungsgruppe und eine Rückreferenz tun . Überprüfen Sie einfach, ob der erste erfasste Wert wiederholt wird.
In der obigen RegExp:
^
und$
steht für Start- und Endanker , um die Position vorherzusagen.(.+)
Erfasst ein beliebiges Muster und erfasst den Wert (außer\n
).\1
ist die Rückreferenz des ersten erfassten Werts und\1+
würde auf Wiederholung des erfassten Werts prüfen.Regex Erklärung hier
Für das RegExp-Debugging verwenden Sie: https://regex101.com/r/pqlAuP/1/debugger
Leistung: https://jsperf.com/reegx-and-loop/13
quelle
If you use normal (TCS:no backreference, concatenation,alternation,Kleene star) regexp and regexp is already compiled then it's O(n).
Aber wie Sie geschrieben haben, verwenden Sie die Rückreferenz. Ist es also immer noch O (n)?[\s\S]
anstelle von verwenden,.
wenn Sie Zeilenumbrüche auf die gleiche Weise wie andere Zeichen abgleichen müssen. Das Punktzeichen stimmt in Zeilenumbrüchen nicht überein. Die Alternative sucht nach allen Leerzeichen und Nicht-Leerzeichen. Dies bedeutet, dass Zeilenumbrüche in die Übereinstimmung einbezogen werden. (Beachten Sie, dass dies schneller als die intuitivere ist(.|[\r\n])
.) Wenn die Zeichenfolge jedoch definitiv keine Zeilenumbrüche enthält, ist die einfache.
am schnellsten. Beachten Sie, dass dies viel einfacher ist, wenn das Dotall-Flag implementiert ist./^(.+?)\1+$/
ein bisschen schneller? (12 Schritte vs 20 Schritte)Der vielleicht schnellste algorithmische Ansatz besteht darin, eine Z-Funktion in linearer Zeit zu erstellen:
C ++ - Implementierung als Referenz:
JavaScript-Implementierung
Optimierungen hinzugefügt - Erstellen einer Hälfte des Z-Arrays und vorzeitiges Beenden
Dann müssen Sie Indizes überprüfen
i
, die n teilen. Wenn Sie dies findeni
, kanni+z[i]=n
die Zeichenfolges
auf die Länge komprimiert werdeni
und Sie können zurückkehrentrue
.Zum Beispiel für
Z-Array ist
und wir können das für finden
Dies
s
könnte als Teilstring der Länge 4 dargestellt werden, der dreimal wiederholt wird.quelle
return z.some((zi, i) => (i + zi) === n && n % i === 0)
const check = (s) => { let n = s.length; let z = Array(n).fill(0); for (let i = 1, l = 0, r = 0; i < n; ++i) { if (i <= r) z[i] = Math.min(r - i + 1, z[i - l]); while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i]; // check condition here and return if (z[i] + i === n && n % i === 0) return true; if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; } // or return false return false; }
Ich habe die Antwort von gnasher729 gelesen und implementiert. Die Idee ist, dass wenn es Wiederholungen gibt, es (auch) eine Primzahl von Wiederholungen geben muss.
Ein etwas anderer Algorithmus ist folgender:
Ich habe die jsPerf-Seite aktualisiert , die die auf dieser Seite verwendeten Algorithmen enthält.
quelle
function*
die zum ersten Mal wie ich stolpern , ist es die Erklärung eines Generators, keine reguläre Funktion. Siehe MDNAngenommen, die Zeichenfolge S hat die Länge N und besteht aus Duplikaten der Teilzeichenfolge s, dann teilt die Länge von s N. Wenn beispielsweise S die Länge 15 hat, hat die Teilzeichenfolge die Länge 1, 3 oder 5.
Sei S aus (p * q) Kopien von s. Dann besteht S auch aus p Kopien von (s, q-mal wiederholt). Wir haben daher zwei Fälle: Wenn N Primzahl oder 1 ist, kann S nur aus Kopien des Teilstrings der Länge 1 hergestellt werden. Wenn N zusammengesetzt ist, müssen wir nur Teilstrings s der Länge N / p auf Primzahlen p-Teilung prüfen die Länge von S.
Bestimmen Sie also N = die Länge von S und finden Sie dann alle seine Primfaktoren in der Zeit O (sqrt (N)). Wenn es nur einen Faktor N gibt, prüfen Sie, ob S dieselbe Zeichenfolge ist, die N-mal wiederholt wird. Andernfalls prüfen Sie für jeden Primfaktor p, ob S aus p Wiederholungen der ersten N / p-Zeichen besteht.
quelle
Ich denke, eine rekursive Funktion könnte auch sehr schnell sein. Die erste Beobachtung ist, dass die maximale Länge des wiederholten Musters halb so lang ist wie die gesamte Zeichenfolge. Und wir könnten einfach alle möglichen wiederholten Musterlängen testen: 1, 2, 3, ..., str.length / 2
Die rekursive Funktion isRepeating (p, str) testet, ob dieses Muster in str wiederholt wird.
Wenn str länger als das Muster ist, erfordert die Rekursion, dass der erste Teil (gleiche Länge wie p) eine Wiederholung ist, ebenso wie der Rest von str. So wird str effektiv in Stücke von Länge p.Länge zerlegt.
Wenn das getestete Muster und str gleich groß sind, endet die Rekursion hier erfolgreich.
Wenn die Länge unterschiedlich ist (passiert für "aba" und das Muster "ab") oder wenn die Teile unterschiedlich sind, wird false zurückgegeben, wodurch die Rekursion weitergegeben wird.
Leistung: https://jsperf.com/reegx-and-loop/13
quelle
if( str===p.repeat(str.length/i) ) return true;
anstatt eine rekursive Funktion zu verwenden?Schrieb dies in Python. Ich weiß, dass es nicht die Plattform ist, aber es hat 30 Minuten gedauert. PS => PYTHON
quelle
Mein Ansatz ähnelt dem von gnasher729, da er die potenzielle Länge des Teilstrings als Hauptfokus verwendet, aber weniger mathematisch und prozessintensiv ist:
L: Länge der ursprünglichen Zeichenfolge
S: Mögliche Längen gültiger Teilzeichenfolgen
Schleife S von (ganzzahliger Teil von) L / 2 bis 1. Wenn L / S eine Ganzzahl ist, überprüfen Sie Ihre ursprüngliche Zeichenfolge anhand der ersten S-Zeichen der ursprünglichen Zeichenfolge, die L / S-mal wiederholt wurden.
Der Grund für das Schleifen von L / 2 rückwärts und nicht von 1 an besteht darin, den größtmöglichen Teilstring zu erhalten. Wenn Sie eine möglichst kleine Teilzeichenfolge von 1 bis L / 2 wünschen. Beispiel: "abababab" hat sowohl "ab" als auch "abab" als mögliche Teilzeichenfolgen. Welche der beiden Methoden schneller ist, wenn Sie sich nur um ein wahres / falsches Ergebnis kümmern, hängt von der Art der Zeichenfolgen / Teilzeichenfolgen ab, auf die dies angewendet wird.
quelle
Der folgende Mathematica-Code erkennt fast, ob die Liste mindestens einmal wiederholt wird. Wenn die Zeichenfolge mindestens einmal wiederholt wird, wird true zurückgegeben. Es kann jedoch auch true zurückgegeben werden, wenn die Zeichenfolge eine lineare Kombination wiederholter Zeichenfolgen ist.
Dieser Code sucht nach dem Beitrag "in voller Länge", der in einer sich wiederholenden Zeichenfolge Null sein muss. Die Zeichenfolge
accbbd
wird jedoch auch als wiederholt betrachtet, da es sich um eine Summe der beiden wiederholten Zeichenfolgenababab
und handelt012012
.Die Idee ist, die schnelle Fourier-Transformation zu verwenden und nach den Frequenzspektren zu suchen. Wenn man sich andere Frequenzen ansieht, sollte man auch dieses seltsame Szenario erkennen können.
quelle
Die Grundidee besteht darin, mögliche Teilzeichenfolgen zu untersuchen, beginnend bei Länge 1 und endend bei der Hälfte der Länge der ursprünglichen Zeichenfolge. Wir betrachten nur Teilzeichenfolgenlängen, die die ursprüngliche Zeichenfolgenlänge gleichmäßig teilen (dh str.length% substring.length == 0).
Diese Implementierung untersucht das erste Zeichen jeder möglichen Teilzeichenfolgeniteration, bevor zum zweiten Zeichen übergegangen wird. Dies kann Zeit sparen, wenn die Teilzeichenfolgen voraussichtlich lang sind. Wenn nach der Untersuchung des gesamten Teilstrings keine Nichtübereinstimmung festgestellt wird, geben wir true zurück.
Wir geben false zurück, wenn wir keine potenziellen Teilzeichenfolgen mehr zur Überprüfung haben.
quelle
Ich bin mit JavaScript nicht vertraut, daher weiß ich nicht, wie schnell dies sein wird, aber hier ist eine lineare Zeitlösung (unter der Annahme einer angemessenen integrierten Implementierung), die nur integrierte Funktionen verwendet. Ich werde den Algorithmus im Pseudocode beschreiben.
Die Idee ähnelt der Antwort von MBo. Für jedes
i
, das die Länge teilt,str
ist eine Wiederholung seiner ersteni
Zeichen genau dann, wenn es nach dem Verschieben füri
Zeichen gleich bleibt .Ich denke, dass ein solches eingebautes System möglicherweise nicht verfügbar oder ineffizient ist. In diesem Fall ist es immer möglich, den KMP-Algorithmus manuell zu implementieren , der ungefähr dieselbe Codemenge benötigt wie der Algorithmus in MBos Antwort.
quelle
i
,s[0:n-i] == s[i:n]
oder äquivalents == s[i:n] + s[0:i]
. Warum muss die zweite Zeile herausfinden, ob sie Wiederholungen hatte?str
an sich selbst zu bildent
, dann Scant
versuchen , finden imstr
Innerent
. Okay, das kann funktionieren (ich habe meine Ablehnung zurückgezogen). In strlen (str) ist es jedoch nicht linear. Saystr
hat die Länge L. Dann wird an jeder Position p = 0,1,2, ... geprüft, ob str [0..L-1] == t [p..p + L-1] O (L) nimmt ) Zeit. Sie müssen O (L) -Prüfungen durchführen, während Sie die Werte von p durchlaufen, also ist es O (L ^ 2).Eine der einfachen Ideen besteht darin, die Zeichenfolge durch die Teilzeichenfolge "" zu ersetzen. Wenn Text vorhanden ist, ist er falsch, andernfalls ist er wahr.
quelle