Votre tâche pour résoudre le problème SLCSC, qui consiste à trouver le code le plus court possible pour résoudre le problème de la conséquence commune la plus longue .
Une solution valable au problème LCS pour deux ou plusieurs chaînes de 1 , ... S n est une chaîne T de longueur maximale telle que les caractères de T apparaissent dans tous les S i , dans le même ordre que dans T .
Notez que T ne doit pas nécessairement être une sous- chaîne de S i .
Exemple
Les chaînes axbycz
et xaybzc
ont 8 sous-séquences communes de longueur 3:
abc abz ayc ayz xbc xbz xyc xyz
N'importe lequel d'entre eux serait une solution valable pour le problème LCS.
Détails
Écrivez un programme ou une fonction qui résout le problème LCS, comme expliqué ci-dessus, en respectant les règles suivantes:
L'entrée consistera en deux chaînes ou plus contenant uniquement des lettres minuscules.
Vous pouvez lire ces chaînes comme un tableau de chaînes ou une chaîne unique avec un délimiteur de votre choix.
Votre code doit générer n'importe laquelle des solutions possibles au problème, éventuellement suivi d'un saut de ligne ou entouré de guillemets.
Vous pouvez supposer que les chaînes sont plus courtes que 1000 caractères et qu'il y a au plus 20 chaînes.
Dans ces limites, votre code devrait fonctionner comme prévu en théorie (avec un temps et une mémoire illimités).
Votre code doit compléter les cas de test combinés de la section suivante en moins d'une heure sur ma machine (Intel Core i7-3770, 16 Go de RAM).
Les approches qui itèrent simplement sur toutes les sous-séquences possibles ne respecteront pas le délai.
L'utilisation de fonctions intégrées qui banalisent cette tâche, comme
LongestCommonSequence
, n'est pas autorisée.
Les règles de code-golf standard s'appliquent.
Cas de test
a
ab
Production: a
aaaaxbbbb
bbbbxcccc
ccccxaaaa
Production: x
hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl
Sortie: hxbbpyhogntqppcqgkxchpsieuhbncvpuqndbjqmclchqyfttdvgoysuhrrl
ou toute autre sous-séquence commune de même longueur
riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg
Sortie: icsvllvjnlktywuar
ou toute autre sous-séquence commune de même longueur
rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr
Sortie: krkk
ou toute autre sous-séquence commune de même longueur
bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja
Sortie: code
ou toute autre sous-séquence commune de même longueur
nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt
Sortie: golf
ou toute autre sous-séquence commune de même longueur
epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp
Sortie: la chaîne vide
la source
Réponses:
CJam, 31
Essayez-le en ligne
9 octets joués au golf grâce à Dennis!
Explication:
Cet algorithme essaie tous les caractères possibles pour la première position de la sous-séquence, remplace chaque chaîne par la sous-chaîne après la première occurrence de ce caractère, puis s'appelle récursivement (avec mémorisation).
la source
Python -
665644Niveaux d'indentation:
Le code définit une fonction
o
, qui prend une liste de chaînes comme arguments et renvoie l'un des LCS pour les chaînes.Code de test:
Temps nécessaire pour exécuter les tests sur mon ordinateur:
la source
(n+1)
, vous pouvez le remplacer par-~n
pour économiser 2 octets dans chaque cas. De plus, partout où vous utilisezmap
unlambda
, envisagez plutôt d'utiliser la compréhension de liste. Par exemple,map(lambda x:x-1,z)
peut être raccourci de trois octets en le changeant en[~-x for x in z]
.r,x=r+(j+1)*x,x*(l[k]+1)
peut être raccourcir+=(j+1)*x;x*=(l[k]+1)
. De plus, vous n'en avez pas besoinu=...
car ilu
n'est utilisé qu'à un seul endroit. Remplacez simplement ce code par la lettreu
.Pyth,
59585535 octetsCoupez 20 octets grâce à @isaacg!
Version 55 octets:
Coupez 3 octets en changeant
.U@bZ
pour@F
(l'opérateur de pliage).Version 58 octets:
Coupez un octet en changeant le format d'un conditionnel booléen.
Version 59 octets:
C'était dur! Python a continué de se briser! Je suis sûr que c'était une sorte de bogue, mais je n'ai pas pu obtenir de test minimal. Tant pis.
J'ai basé l'algorithme sur celui-ci . Ce qui est bien, sauf que celui-ci est conçu pour seulement deux cordes. J'ai dû le modifier un peu pour le faire fonctionner davantage. Ensuite, le dernier cas de test prenait trop de temps, j'ai donc dû ajouter une vérification supplémentaire pour quitter la récursivité s'il n'y a plus de caractères communs.
C'est assez lent, mais cela devrait prendre moins d'une heure (j'espère). Je teste mon Core i3 avec 6 Go de RAM, donc votre Core i7 16 Go devrait souffler. :)
J'ai également profité des fonctions de mémorisation automatique de Pyth pour le rendre un peu plus rapide.
EDIT : @Dennis a dit que ça passe!
Pour le tester, ajoutez la ligne suivante:
et lui donner une liste de chaînes via une entrée standard (par exemple
['a', 'ab']
).Explication pour la version 35 octets:
WIP.
Explication pour la version 55 octets:
la source
C,
618564 octetsEt ici, il est démêlé, pour la "lisibilité":
Mesdames et messieurs, j'ai fait une horrible erreur. Il permet d'être plus joli ... Et goto-less ... Au moins , il est maintenant rapide .
Nous définissons une fonction récursive
L
qui prend en entrée un tableaus
de tableaux de caractères et le nombren
de chaînes. La fonction renvoie la chaîne résultante sur stdout et renvoie accessoirement la taille en caractères de cette chaîne.L'approche
Bien que le code soit alambiqué, la stratégie ici n'est pas trop complexe. Nous commençons par un algorithme récursif assez naïf, que je décrirai avec un pseudocode:
Maintenant, cet algorithme en lui-même est assez atroce (mais peut tenir dans environ 230 octets, j'ai trouvé). Ce n'est pas ainsi que l'on obtient des résultats rapides. Cet algorithme évolue incroyablement mal avec la longueur de la chaîne. Cet algorithme ne , cependant, échelle assez bien avec un plus grand nombre de chaînes. Le dernier cas de test serait résolu pratiquement instantanément, car aucune chaîne de caractères
s
n'a de caractèrec
en commun. J'ai mis en œuvre deux astuces principales ci-dessus qui ont entraîné une augmentation de vitesse incroyable:À chaque appel à
L
, vérifiez si nous avons déjà reçu cette même entrée. Étant donné que dans la pratique, les informations sont transmises via des pointeurs vers le même ensemble de chaînes, nous n'avons pas réellement à comparer des chaînes, uniquement des emplacements, ce qui est génial. Si nous constatons que nous avons obtenu ces informations auparavant, il n'est pas nécessaire de parcourir les calculs (la plupart du temps, mais obtenir des résultats rend les choses un peu plus compliquées) et nous pouvons nous en sortir en renvoyant simplement la longueur. Si nous ne trouvons pas de correspondance, enregistrez cet ensemble d'entrées / sorties pour le comparer aux appels futurs. Dans le code C, la deuxièmefor
boucle tente de trouver des correspondances avec l'entrée. Les pointeurs d'entrée connus sont enregistrés dansR
, et la longueur correspondante et les valeurs de sortie des caractères sont stockées dansA
. Ce plan a eu un effet drastique sur l'exécution, en particulier avec des chaînes plus longues.Chaque fois que nous trouvons les emplacements
c
danss
, il y a une chance que nous savons dès le départ que ce que nous avons trouvé est pas optimale. Si chaque emplacement dec
apparaît après un emplacement connu d'une autre lettre, nous savons automatiquement que celac
ne conduit pas à une sous-chaîne optimale, car vous pouvez y insérer une lettre de plus. Cela signifie que pour un petit coût, nous pouvons potentiellement supprimer plusieurs centaines d'appels àL
de grandes chaînes. Dans le code C ci-dessus,y
est un indicateur défini si nous savons automatiquement que ce caractère conduit à une chaîne sous-optimale, etz
est un indicateur défini si nous trouvons un caractère qui a des apparences exclusivement antérieures à tout autre caractère connu. Les premières apparitions actuelles des personnages sont stockées dansx
. L'implémentation actuelle de cette idée est un peu compliquée, mais les performances ont presque doublé dans de nombreux cas.Avec ces deux idées, ce qui n'a pas fini en une heure prend maintenant environ 0,015 seconde.
Il y a probablement beaucoup plus de petites astuces qui peuvent accélérer les performances, mais à ce stade, j'ai commencé à m'inquiéter de ma capacité à tout jouer au golf. Je ne suis toujours pas satisfait du golf, donc j'y reviendrai probablement plus tard!
Timings
Voici un code de test, que je vous invite à essayer en ligne :
J'ai exécuté les cas de test de l'OP sur un ordinateur portable équipé d'une puce Intel Core i7 à 1,7 GHz, avec un paramètre d'optimisation de
-Ofast
. La simulation a signalé un pic de 712 Ko requis. Voici un exemple d'exécution de chaque scénario de test, avec des horaires:En golf, j'ai atteint les performances de manière assez significative, et comme les gens semblaient aimer la vitesse brute (0,013624 s pour terminer tous les cas de test combinés) de ma précédente solution de 618 octets, je vais la laisser ici pour référence:
L'algorithme lui-même est inchangé, mais le nouveau code repose sur des divisions et des opérations au niveau du bit plus délicates qui finissent par ralentir le tout.
la source
best_found
? Il n'est mentionné que deux fois, une fois lorsqu'il est utilisé au conditionnel et l'autre lorsqu'il est réinitialisé.best_found
soit réglé surbest_length + current_depth
. Vous devriez probablement mentionner cela dans l'explication!best_found
est un entier global qui décrit la longueur de la sous-chaîne commune la plus longue trouvée à un moment donné de l'exécution. Je vais mettre ça dans l'explication!Python 2, 285
Code:
Usage:
Explication:
Il s'agit d'une fonction récursive.
s
est le personnage que nous recherchons.a
contient la liste des chaînes coupées aprèss
.b
contient une liste de chaînes non encore couplée.f
renvoie la chaîne commune la plus longue.La première condition vérifie si nous avons fini de parcourir toutes les chaînes. si c'est le cas, cela signifie
s
est un caractère commun, et il revients
et recherche des caractères plus communs.La deuxième condition vérifie si nous ne commençons pas à parcourir une chaîne, ce qui signifie que nous n'avons même pas de caractère (
a==[]
est équivalent às==''
). si c'est le cas, nous vérifions chaque caractère de la première chaîneb
.La dernière ligne déplace la première chaîne
b
versa
, en trouvant chaque occurrence des
dans cette chaîne.Au premier appel,
s
devrait être une chaîne vide.a
devrait être une liste vide etb
devrait contenir toutes les chaînes.la source
f(b,s='',a=[])
.