Ihre Aufgabe zur Lösung des SLCSC-Problems besteht darin, den kürzestmöglichen Code zur Lösung des Problems der längsten gemeinsamen Folge zu finden .
Eine gültige Lösung für das LCS-Problem für zwei oder mehr Zeichenfolgen S 1 ,… S n ist eine beliebige Zeichenfolge T mit maximaler Länge, so dass die Zeichen von T in allen S i in derselben Reihenfolge wie in T erscheinen .
Beachten Sie, dass T nicht Teil sein muss Zeichenfolge von S i .
Beispiel
Die Strings axbycz
und xaybzc
haben 8 gemeinsame Teilsequenzen der Länge 3:
abc abz ayc ayz xbc xbz xyc xyz
All dies wäre eine gültige Lösung für das LCS-Problem.
Einzelheiten
Schreiben Sie ein Programm oder eine Funktion, die das LCS-Problem löst, wie oben erläutert, und befolgen Sie dabei die folgenden Regeln:
Die Eingabe besteht aus zwei oder mehr Zeichenfolgen, die nur Kleinbuchstaben enthalten.
Sie können diese Zeichenfolgen als Array von Zeichenfolgen oder als einzelne Zeichenfolge mit einem Trennzeichen Ihrer Wahl lesen.
Ihr Code muss eine einzelne der möglichen Lösungen für das Problem ausgeben, optional gefolgt von einem Zeilenvorschub oder umgeben von Anführungszeichen.
Sie können davon ausgehen, dass die Zeichenfolgen kürzer als 1000 Zeichen sind und dass höchstens 20 Zeichenfolgen vorhanden sind.
Innerhalb dieser Grenzen sollte Ihr Code theoretisch wie erwartet funktionieren (bei unbegrenzter Zeit und unbegrenztem Speicher).
Ihr Code muss die kombinierten Testfälle des nächsten Abschnitts in weniger als einer Stunde auf meinem Computer (Intel Core i7-3770, 16 GiB RAM) abschließen.
Ansätze, die einfach alle möglichen Teilsequenzen durchlaufen, halten das Zeitlimit nicht ein.
Die Verwendung von integrierten Funktionen, die diese Aufgabe trivialisieren, z. B.
LongestCommonSequence
, ist nicht zulässig.
Es gelten die Standardregeln für Code-Golf .
Testfälle
a
ab
Ausgabe: a
aaaaxbbbb
bbbbxcccc
ccccxaaaa
Ausgabe: x
hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl
Ausgabe: hxbbpyhogntqppcqgkxchpsieuhbncvpuqndbjqmclchqyfttdvgoysuhrrl
oder eine andere gemeinsame Teilsequenz gleicher Länge
riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg
Ausgabe: icsvllvjnlktywuar
oder eine andere gemeinsame Teilsequenz gleicher Länge
rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr
Ausgabe: krkk
oder eine andere gemeinsame Teilsequenz gleicher Länge
bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja
Ausgabe: code
oder eine andere gemeinsame Teilsequenz gleicher Länge
nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt
Ausgabe: golf
oder eine andere gemeinsame Teilsequenz gleicher Länge
epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp
Ausgabe: die leere Zeichenfolge
quelle
Antworten:
CJam, 31
Probieren Sie es online aus
9 Bytes Golf dank Dennis!
Erläuterung:
Dieser Algorithmus versucht jedes mögliche Zeichen für die erste Position der Teilsequenz, ersetzt jede Zeichenfolge nach dem ersten Auftreten dieses Zeichens durch die Teilzeichenfolge und ruft sich dann rekursiv auf (mit Memoisierung).
quelle
Python -
665644Einrückungsstufen:
Der Code definiert eine Funktion
o
, die eine Liste von Zeichenfolgen als Argumente verwendet und eine der LCS für die Zeichenfolgen zurückgibt.Testcode:
Zeit, die benötigt wird, um die Tests auf meinem Computer auszuführen:
quelle
(n+1)
, diese ersetzen-~n
, um jeweils 2 Bytes zu sparen. Erwägen Sie außerdem, überall dort, wo Siemap
mit a verwendenlambda
, stattdessen das Listenverständnis zu verwenden. Zum Beispielmap(lambda x:x-1,z)
kann durch Ändern um drei Bytes verkürzt werden[~-x for x in z]
.r,x=r+(j+1)*x,x*(l[k]+1)
kann auf verkürzt werdenr+=(j+1)*x;x*=(l[k]+1)
. Außerdem brauchen Sie nicht,u=...
weilu
nur an einem Ort verwendet wird. Ersetzen Sie einfach den Buchstaben durch diesen Codeu
.Pyth,
59585535 BytesSchneiden Sie dank @isaacg satte 20 Bytes!
55-Byte-Version:
Schneiden Sie 3 Bytes ab, indem Sie
.U@bZ
zu@F
(dem Faltoperator) wechseln .58-Byte-Version:
Schneiden Sie ein Byte ab, indem Sie das Format einer booleschen Bedingung ändern.
59-Byte-Version:
Das war schwer! Python hat immer wieder Fehler gemacht! Ich bin mir ziemlich sicher, dass es eine Art Fehler war, aber ich konnte keinen minimalen Testfall bekommen. Naja.
Ich habe den Algorithmus auf diesem basiert . Was in Ordnung ist, außer dass diese nur für zwei Saiten ausgelegt ist. Ich musste es ein wenig optimieren, damit es mehr funktioniert. Dann dauerte der letzte Testfall zu lange, sodass ich eine zusätzliche Prüfung hinzufügen musste, um die Rekursion zu beenden, wenn keine gemeinsamen Zeichen mehr vorhanden sind.
Es ist ziemlich langsam, sollte aber (hoffentlich) weniger als eine Stunde dauern. Ich teste auf meinem Core i3 mit 6 GB RAM, daher sollte Ihr 16 GB Core i7 dies durchbrechen. :) :)
Ich habe auch Pyth's Auto-Memoizing-Funktionen genutzt, um es ein bisschen schneller zu machen.
EDIT : @ Tennis sagte, es geht vorbei!
Fügen Sie zum Testen die folgende Zeile hinzu:
und geben Sie ihm eine Liste von Zeichenfolgen über Standardeingabe (z
['a', 'ab']
. B. ).Erläuterung zur 35-Byte-Version:
WIP.
Erläuterung zur 55-Byte-Version:
quelle
C
618564 BytesUnd hier wird es für "Lesbarkeit" enträtselt:
Meine Damen und Herren, ich habe einen schrecklichen Fehler gemacht. Es verwendet bisschen besser zu sein ... Und gehe weniger ... Spätestens jetzt ist es schnell .
Wir definieren eine rekursive Funktion
L
, die ein Arrays
von Zeichenarrays und die Anzahln
der Zeichenfolgen als Eingabe verwendet. Die Funktion gibt die resultierende Zeichenfolge an stdout aus und gibt im Übrigen die Größe in Zeichen dieser Zeichenfolge zurück.Die Vorgehensweise
Obwohl der Code kompliziert ist, ist die Strategie hier nicht allzu komplex. Wir beginnen mit einem eher naiven rekursiven Algorithmus, den ich mit Pseudocode beschreiben werde:
Nun, dieser Algorithmus alleine ist ziemlich grausam (kann aber in ungefähr 230 Bytes passen, wie ich gefunden habe). So erhält man keine schnellen Ergebnisse. Dieser Algorithmus skaliert unglaublich schlecht mit der Stringlänge. Dieser Algorithmus ist jedoch skaliert ziemlich gut mit einer größeren Anzahl von Strings. Der letzte Testfall würde praktisch sofort gelöst, da keine Zeichenfolgen
s
Zeichenc
gemeinsam haben. Es gab zwei Haupttricks, die ich oben implementiert habe und die zu einer unglaublichen Geschwindigkeitssteigerung führten:L
Überprüfen Sie bei jedem Anruf bei , ob wir zuvor dieselbe Eingabe erhalten haben. Da in der Praxis wird über Zeiger auf den gleichen Satz von Saiten Informationen herumgereicht, haben wir nicht wirklich haben Strings zu vergleichen, nur Orte, die groß ist. Wenn wir feststellen, dass wir diese Informationen bereits erhalten haben, müssen wir die Berechnungen nicht durchgehen (meistens, aber das Abrufen der Ausgabe macht dies etwas komplizierter), und wir können davonkommen, indem wir nur die Länge zurückgeben. Wenn wir uns nicht ein Spiel finden, speichert diesen Satz von Eingabe / Ausgabe für künftige Anrufe zu vergleichen. Im C-Code versucht die zweitefor
Schleife, Übereinstimmungen mit der Eingabe zu finden. Bekannte Eingabezeiger werden in gespeichertR
und die entsprechenden Längen- und Zeichenausgabewerte werden in gespeichertA
. Dieser Plan hatte drastische Auswirkungen auf die Laufzeit, insbesondere bei längeren Zeichenfolgen.Jedes Mal, wenn wir die Standorte von
c
ins
finden, besteht die Möglichkeit, dass wir sofort wissen, dass das, was wir gefunden haben, nicht optimal ist. Wenn jede Positionc
erscheint nach einigem bekannten Standort eines anderen Briefes, wir wissen automatisch , dass diesc
nicht zu einer optimalen Teilkette führt, weil Sie einen weiteren Buchstaben in ihm passen. Dies bedeutet, dass wir für geringe Kosten möglicherweise mehrere hundert AnrufeL
für große Zeichenfolgen entfernen können . Im obigen C-Codey
ist ein Flag gesetzt, wenn wir automatisch wissen, dass dieses Zeichen zu einer suboptimalen Zeichenfolge führt, undz
ist ein Flag gesetzt, wenn wir ein Zeichen finden, das ausschließlich früher als jedes andere bekannte Zeichen erscheint. Die aktuell frühesten Erscheinungen von Zeichen werden in gespeichertx
. Die derzeitige Umsetzung dieser Idee ist etwas chaotisch, hat aber in vielen Fällen die Leistung nahezu verdoppelt.Mit diesen beiden Ideen dauerte das, was in einer Stunde nicht fertig war, ungefähr 0,015 Sekunden.
Es gibt wahrscheinlich noch viel mehr kleine Tricks, die die Leistung beschleunigen können, aber zu diesem Zeitpunkt begann ich mir Sorgen um meine Fähigkeit zu machen, alles zu spielen. Ich bin immer noch nicht zufrieden mit dem Golf, also werde ich wahrscheinlich später darauf zurückkommen!
Timings
Hier ist ein Testcode, den ich einlade, online zu testen :
Ich habe die Testfälle des OP auf einem Laptop ausgeführt, der mit einem 1,7-GHz-Intel Core i7-Chip mit einer Optimierungseinstellung von ausgestattet ist
-Ofast
. Die Simulation ergab einen Spitzenwert von 712 KB. Hier ist ein Beispiellauf für jeden Testfall mit Zeitangaben:Beim Golfen habe ich die Leistung ziemlich stark beeinträchtigt, und da die Leute die rohe Geschwindigkeit (0,013624 s, um alle Testfälle zusammen abzuschließen) meiner vorherigen 618-Byte-Lösung zu mögen schienen, lasse ich sie hier als Referenz:
Der Algorithmus selbst bleibt unverändert, aber der neue Code basiert auf Unterteilungen und einigen schwierigeren bitweisen Operationen, die das Ganze verlangsamen.
quelle
best_found
? Es wird nur zweimal erwähnt, einmal, wenn es in der Bedingung verwendet wird, und das andere Mal, wenn es zurückgesetzt wird.best_found
so eingestellt zu seinbest_length + current_depth
. Das sollten Sie wohl in der Erklärung erwähnen!best_found
ist eine globale Ganzzahl, die die Länge des längsten gemeinsamen Teilstrings beschreibt, der zu einem bestimmten Zeitpunkt in der Ausführung gefunden wurde. Ich werde das in die Erklärung setzen!Python 2, 285
Code:
Verwendung:
Erläuterung:
Dies ist eine rekursive Funktion.
s
ist der Charakter, den wir suchen.a
enthält eine Liste der nachgeschnittenen Zeichenfolgens
.b
enthält eine Liste der noch nicht gehandelten Zeichenfolgen.f
Gibt die längste gemeinsame Zeichenfolge zurück.Die erste Bedingung prüft, ob wir alle Zeichenfolgen durchlaufen haben. Wenn ja, bedeutet dies, dass es sich
s
um ein gemeinsames Zeichen handelt, und es kehrt zurücks
und sucht nach häufigeren Zeichen.Die zweite Bedingung prüft, ob wir keine Zeichenfolge durchlaufen, was bedeutet, dass wir nicht einmal ein Zeichen haben (
a==[]
entsprichts==''
). Wenn ja, überprüfen wir jedes Zeichen der ersten Zeichenfolge inb
.Die letzte Zeile bewegt den ersten String in
b
zua
, indem jedes Vorkommen zu finden ,s
in dieser Zeichenfolge.Beim ersten Aufruf
s
sollte die Zeichenfolge leer sein.a
sollte eine leere Liste sein undb
sollte alle Zeichenfolgen enthalten.quelle
f(b,s='',a=[])
.