Su tarea es resolver el problema SLCSC, que consiste en encontrar el código más corto posible para resolver el problema de la subsecuencia común más larga .
Una solución válida al problema LCS para dos o más cadenas S 1 , ... S n es cualquier cadena de T de la longitud máxima de tal manera que los personajes de T aparecen en todos los S i , en el mismo orden que en T .
Tenga en cuenta que T no tiene que ser una subcadena de S i .
Ejemplo
Las cadenas axbycz
y xaybzc
tienen 8 subsecuencias comunes de longitud 3:
abc abz ayc ayz xbc xbz xyc xyz
Cualquiera de estos sería una solución válida para el problema de LCS.
Detalles
Escriba un programa o una función que resuelva el problema de LCS, como se explicó anteriormente, respetando las siguientes reglas:
La entrada constará de dos o más cadenas que contienen solo letras minúsculas.
Puede leer esas cadenas como una matriz de cadenas o una sola cadena con un delimitador de su elección.
Su código debe mostrar cualquiera de las posibles soluciones al problema, opcionalmente seguido de un salto de línea o rodeado de comillas.
Puede suponer que las cadenas tienen menos de 1000 caracteres y que hay como máximo 20 cadenas.
Dentro de estos límites, su código debería funcionar como se esperaba en teoría (dado tiempo y memoria ilimitados).
Su código debe completar los casos de prueba combinados de la siguiente sección en menos de una hora en mi máquina (Intel Core i7-3770, 16 GiB RAM).
Los enfoques que simplemente iteran sobre todas las subsecuencias posibles no cumplirán con el límite de tiempo.
LongestCommonSequence
No se permite el uso de elementos integrados que trivializan esta tarea, como por ejemplo .
Se aplican reglas estándar de código de golf .
Casos de prueba
a
ab
Salida: a
aaaaxbbbb
bbbbxcccc
ccccxaaaa
Salida: x
hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl
Salida: hxbbpyhogntqppcqgkxchpsieuhbncvpuqndbjqmclchqyfttdvgoysuhrrl
o cualquier otra subsecuencia común de la misma longitud
riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg
Salida: icsvllvjnlktywuar
o cualquier otra subsecuencia común de la misma longitud
rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr
Salida: krkk
o cualquier otra subsecuencia común de la misma longitud
bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja
Salida: code
o cualquier otra subsecuencia común de la misma longitud
nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt
Salida: golf
o cualquier otra subsecuencia común de la misma longitud
epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp
Salida: la cadena vacía
fuente
Respuestas:
CJam, 31
Pruébalo en línea
¡9 bytes de golf gracias a Dennis!
Explicación:
Este algoritmo prueba todos los caracteres posibles para la primera posición de la subsecuencia, reemplaza cada cadena con la subcadena después de la primera aparición de ese carácter y luego se llama a sí misma de forma recursiva (con memorización).
fuente
Python -
665644Niveles de sangría:
El código define una función
o
, que toma una lista de cadenas como argumentos y devuelve uno de los LCS para las cadenas.Código de prueba:
Tiempo que lleva ejecutar las pruebas en mi computadora:
fuente
(n+1)
, puede reemplazarlo-~n
para guardar 2 bytes en cada caso. Además, en cualquier lugar que usemap
con unlambda
, considere usar la comprensión de la lista. Por ejemplo,map(lambda x:x-1,z)
puede acortarse en tres bytes cambiándolo a[~-x for x in z]
.r,x=r+(j+1)*x,x*(l[k]+1)
se puede acortar ar+=(j+1)*x;x*=(l[k]+1)
. Además, no es necesariou=...
porqueu
solo se usa en un lugar. Simplemente sustituya ese código por la letrau
.Pyth,
59585535 bytes¡Corta la friolera de 20 bytes gracias a @isaacg!
Versión de 55 bytes:
Corte 3 bytes cambiando
.U@bZ
a@F
(el operador de plegado).Versión de 58 bytes:
Corte un byte cambiando el formato de un condicional booleano.
Versión de 59 bytes:
Esto fue dificil! Python siguió segfaulting! Estoy bastante seguro de que fue algún tipo de error, pero no pude obtener un caso de prueba mínimo. Oh bien.
Basé el algoritmo en este . Lo cual está bien, excepto que ese está diseñado para solo dos cadenas. Tuve que ajustarlo un poco para que funcione más. Luego, el último caso de prueba tardó demasiado, así que tuve que agregar una verificación adicional para salir de la recursividad si no hay más caracteres comunes.
Es bastante lento, pero debería tomar menos de una hora (con suerte). Estoy probando en mi Core i3 con 6 GB de RAM, por lo que su Core i7 de 16 GB debería superar esto. :)
También aproveché las funciones de auto-memorización de Pyth para hacerlo un poco más rápido.
EDITAR : @Dennis dijo que pasa!
Para probarlo, agregue la siguiente línea:
y darle una lista de cadenas a través de la entrada estándar (por ejemplo
['a', 'ab']
).Explicación para la versión de 35 bytes:
WIP
Explicación para la versión de 55 bytes:
fuente
C,
618564 bytesY aquí está descifrado, por "legibilidad":
Damas y caballeros, he cometido un error horrible. Se utiliza para ser más bonita ... y Goto-menos ... Al menos ahora es rápida .
Definimos una función recursiva
L
que toma como entrada una matrizs
de matrices de caracteres y el númeron
de cadenas. La función genera la cadena resultante en stdout y, por cierto, devuelve el tamaño en caracteres de esa cadena.El enfoque
Aunque el código es complicado, la estrategia aquí no es demasiado compleja. Comenzamos con un algoritmo recursivo bastante ingenuo, que describiré con pseudocódigo:
Ahora, este algoritmo por sí solo es bastante atroz (pero he encontrado unos 230 bytes). No es así como se obtienen resultados rápidos. Este algoritmo escala increíblemente mal con la longitud de la cadena. Este algoritmo tiene , sin embargo, la escala bastante bien con un mayor número de cadenas. El último caso de prueba se resolvería prácticamente al instante, ya que las cadenas no
s
tienen ningún carácterc
en común. Hubo dos trucos principales que implementé anteriormente que resultaron en un increíble aumento de velocidad:En cada llamada a
L
, verifique si se nos ha dado esta misma entrada antes. Como en la práctica la información se transmite a través de punteros al mismo conjunto de cadenas, en realidad no tenemos que comparar cadenas, solo ubicaciones, lo cual es genial. Si descubrimos que obtuvimos esta información antes, no hay necesidad de ejecutar los cálculos (la mayoría de las veces, pero obtener resultados hace que esto sea un poco más complicado) y podemos salir con solo devolver la longitud. Si no encontramos una coincidencia, guarde este conjunto de entrada / salida para compararlo con futuras llamadas. En el código C, el segundofor
ciclo intenta encontrar coincidencias con la entrada. Los punteros de entrada conocidos se guardanR
y los valores de salida de caracteres y longitud correspondientes se almacenan enA
. Este plan tuvo un efecto drástico en el tiempo de ejecución, especialmente con cadenas más largas.Cada vez que encontramos las ubicaciones de
c
ins
, existe la posibilidad de que sepamos de inmediato que lo que hemos encontrado no es óptimo. Si cada ubicación dec
aparece después de alguna ubicación conocida de otra letra, sabemos automáticamente que estoc
no conduce a una subcadena óptima, porque puede incluir una letra más en ella. Esto significa que por un pequeño costo, potencialmente podemos eliminar varios cientos de llamadas aL
cadenas grandes. En el código C anterior,y
hay un conjunto de indicadores si sabemos automáticamente que este carácter conduce a una cadena subóptima, yz
es un conjunto de indicadores si encontramos un carácter que tiene apariencias exclusivamente anteriores a cualquier otro carácter conocido. Las primeras apariencias actuales de los personajes se almacenan enx
. La implementación actual de esta idea es un poco desordenada, pero casi duplica el rendimiento en muchos casos.Con estas dos ideas, lo que no terminó en una hora ahora tomó alrededor de 0.015 segundos.
Probablemente hay muchos más trucos que pueden acelerar el rendimiento, pero en este punto comencé a preocuparme por mi capacidad para jugar golf todo. Todavía no estoy contento con el golf, ¡así que probablemente volveré a esto más tarde!
Tiempos
Aquí hay un código de prueba, que te invito a probar en línea :
Ejecuté los casos de prueba del OP en una computadora portátil equipada con un chip Intel Core i7 de 1.7 GHz, con una configuración de optimización de
-Ofast
. La simulación informó un pico de 712 KB requerido. Aquí hay un ejemplo de cada caso de prueba, con tiempos:En el golf, alcancé el rendimiento de manera bastante significativa, y dado que a la gente parecía gustarle la velocidad bruta (0.013624 s para completar todos los casos de prueba combinados) de mi solución anterior de 618 bytes, lo dejaré aquí como referencia:
El algoritmo en sí no ha cambiado, pero el nuevo código se basa en divisiones y algunas operaciones bit a bit más complicadas que terminan ralentizando todo.
fuente
best_found
? Solo se menciona dos veces, una cuando se usa en condicional y la otra cuando se restablece.best_found
está configurado comobest_length + current_depth
. ¡Probablemente deberías mencionar eso en la explicación!best_found
es un número entero global que describe la longitud de la subcadena común más larga encontrada en cualquier punto dado de ejecución. ¡Lo pondré en la explicación!Pitón 2, 285
Código:
Uso:
Explicación:
Esta es una función recursiva.
s
es el personaje que estamos buscando.a
contiene una lista de cadenas cortadas despuéss
.b
contiene una lista de cadenas sin tocar todavía.f
devuelve la cadena común más larga.La primera condición verifica si hemos terminado de pasar por todas las cadenas. si es así, significa que
s
es un carácter común, y regresas
y busca caracteres más comunes.La segunda condición verifica si no comenzamos a pasar por ninguna cadena, lo que significa que ni siquiera tenemos un carácter (
a==[]
es equivalente as==''
). si es así, verificamos cada carácter de la primera cadenab
.La última línea mueve la primera cadena
b
hacia adentroa
, al encontrar cada aparición des
en esta cadena.En la primera llamada,
s
debe ser una cadena vacía.a
debe estar lista vacía yb
debe contener todas las cadenas.fuente
f(b,s='',a=[])
.