Su objetivo: dada una cadena de paréntesis, genere la distancia mínima Damerau-Levenshtein requerida para convertir la cadena de entrada en una cadena donde los corchetes estén equilibrados.
Entrada
La cadena de entrada solo contendrá paréntesis y ningún otro carácter. Es decir, es una combinación de cualquiera de los personajes en (){}[]<>
. Puede tomar la entrada como una cadena o una matriz de caracteres. No puede hacer otras suposiciones sobre la cadena de entrada; puede ser arbitrariamente largo (hasta el tamaño máximo admitido por su idioma), puede estar vacío, los corchetes ya pueden estar equilibrados, etc.
Damerau-Levenshtein Distancia
La distancia Damerau-Levenshtein entre dos cadenas es el número mínimo de inserciones, eliminaciones, sustituciones de un solo carácter y transposiciones (intercambio) de dos caracteres adyacentes.
Salida
La salida debe ser la distancia mínima de Damerau-Levenshtein entre la cadena de entrada y una cadena en la que coinciden los corchetes. La salida debe ser un número , no la cadena equilibrada resultante.
Un par de paréntesis se considera "coincidente" si los paréntesis de apertura y cierre están en el orden correcto y no tienen caracteres dentro de ellos, como
()
[]{}
O si cada subelemento dentro de él también coincide.
[()()()()]
{<[]>}
(()())
Los subelementos también pueden anidarse en varias capas de profundidad.
[(){<><>[()]}<>()]
<[{((()))}]>
(Gracias a @DJMcMayhem por la definición)
Casos de prueba
Input Possible Balanced Output
Empty Empty 0
[](){}<> [](){}<> 0
[(){}<> [(){}<>] 1
[(]) []() 1
[[[[[[[[ [][][][] 4
(](<>}[>(}>><(>(({}] ()(<>)[(<><>){}] 7
>]{])< []{()} 3
([)}}>[ (){}<> 4
{<((<<][{{}>[<) <>(<<[]>{}>[]) 5
{><({((})>}}}{(}} {<><({()})>}{}{()} 4
(](<)>}[>(}>>{]<<(]] (<()<><<>()>>[])<()> 9
}})( {}() 2
(Gracias a @WheatWizard por resolver la mitad de los casos de prueba)
Este es el código de golf , ¡la menor cantidad de bytes gana!
Sus envíos deben ser comprobables, lo que significa que debe generar un resultado para cada caso de prueba en no más de una hora.
[<>]
o[]<>
o<>
Respuestas:
Retina,
254252264248240232267 bytesGracias a @AnthonyPham, @officialaimm y @MistahFiggins por señalar errores
Pruébalo en línea!
Solución de fuerza no bruta! Funciona para todos los casos de prueba, e incluso encontró un error en uno.
-2 bytes gracias a @MartinEnder (
${4}
a$+
)+12 bytes para tener en cuenta los casos de intercambio adicionales
-16 bytes haciendo un mejor uso de las clases de caracteres
-8 bytes eliminando una restricción innecesaria en el intercambio. Esto también solucionó un error :)
-10 bytes combinando la lógica de intercambio en una sola expresión regular
+2 bytes para tener en cuenta los intercambios consecutivos
+ muchos para varias correcciones de errores **
Explicación:
T`[]()`:;'"
se utiliza para reemplazar tipos de soportes especiales por conveniencia. Primero, reemplazamos recursivamente todos los corchetes coincidentes con-
,AB
o69
dependiendo de si son adyacentes o no.Luego, se realiza un "intercambio" útil eliminando los corchetes recién emparejados y agregando un
1
al comienzo de la cadena. También reemplazamos-
con la cadena vacía, ya que solo se estaba utilizando para el intercambio anterior.A continuación, intentamos "reemplazos" eliminando pares de corchetes no coincidentes que no se superponen entre corchetes ya emparejados y agregando un
1
a la cadena.Finalmente,
\W|6B|1
cuenta cualquier paréntesis restante más el número de1
s.** Actualmente estoy trabajando en una versión más corta que utiliza las funciones de división de línea de Retina, aunque me encontré con un problema considerable, por lo que podría llevar bastante tiempo.
fuente
${4}
se puede acortar a$+
. Sin embargo, tengo mucha curiosidad por qué esto garantiza que funcione.[{][}] [] [[][][][][][]] [][][][][][][][][][][][]
, simplemente puede cambiar el}
interior del primer par de paréntesis y equilibrarlo. El espaciado se usa para hacer que la entrada sea un poco más legible. Sacaste 3 pero realmente debería ser uno.[{]}
devuelve 1 pero[{][]}
devuelve 2.Brain-Flak , 1350 bytes
Pruébalo en línea!
Con comparaciones de velocidad constante y desreferencia de puntero, este algoritmo es O (n 3 ). Desafortunadamente, Brain-Flak no tiene ninguno de estos, por lo que este programa se ejecuta en tiempo O (n 5 ). El caso de prueba más largo dura unos 15 minutos.
Simplificando resultados
Para ver que mi algoritmo funciona, necesitamos mostrar algunos resultados que reducen considerablemente el espacio de búsqueda. Estos resultados se basan en el hecho de que el objetivo es un idioma completo en lugar de solo una cadena específica.
No se necesitan inserciones. En cambio, puede eliminar el corchete con el que el carácter insertado eventualmente coincidiría.
Nunca necesitará quitar un soporte, luego intercambiar sus dos vecinos. Para ver esto, asumir sin pérdida de generalidad que el soporte eliminada es
(
, por lo estamos transformandoa(c
aca
en dos pasos. Al cambiarc
e insertar una copia, podemos llegarca()
en dos pasos sin un intercambio. (Esta inserción se puede eliminar mediante la regla anterior).Nunca será necesario cambiar el mismo soporte dos veces. Este es un hecho estándar sobre la distancia Damerau-Levenshtein en general.
Otro resultado simplificador que no utilicé, porque contabilizarlos costaría bytes:
El algoritmo
Cuando cualquier cadena se reduce a una cadena equilibrada, una de las siguientes será verdadera:
k
(posiblemente después de cambiar uno o ambos).k
.En el segundo caso, el soporte en la posición
k
puede haber cambiado con uno de sus vecinos. En cualquiera de los dos últimos casos, la cadena entre el primer corchete (posiblemente nuevo) y el corchete que comenzó en posiciónk
debe editarse en una cadena equilibrada, al igual que la cadena que consiste en todo lo que siguek
.Esto significa que se puede utilizar un enfoque de programación dinámica. Dado que un paréntesis intercambiado no necesita intercambiarse nuevamente, solo necesitamos considerar subcadenas contiguas, así como subsecuencias formadas al eliminar el segundo carácter y / o el penúltimo carácter de dicha subcadena. Por lo tanto, solo hay O (n 2 ) subsecuencias que debemos observar. Cada uno de ellos tiene O (n) formas posibles de unir (o eliminar) el primer paréntesis, por lo que el algoritmo sería O (n 3 ) en las condiciones anteriores.
La estructura de datos
La pila derecha incluye los corchetes de la cadena original, con dos bytes por corchete. La primera entrada determina el corchete completo, y se elige de manera que los corchetes coincidentes tengan una diferencia de exactamente 1. La segunda entrada solo determina si es un corchete de apertura o un corchete de cierre: esto determina cuántos cambios se necesitan para que dos corchetes coincidan El uno al otro. No se hacen explícitos los ceros implícitos debajo de esto, de modo que podamos usar
[]
para obtener la longitud total de esta cadena.Cada subcadena considerada está representada por dos números en el rango de 0 a 2n: uno para la posición inicial y otro para el final. La interpretación es la siguiente:
2k
comenzará en la posiciónk
(indexada a 0) y el segundo carácter no se eliminará.2k+1
comenzará en la posiciónk
, y el segundo carácter se elimina debido a que se ha cambiado a la izquierda.2k
terminará justo antes de la posiciónk
(es decir, el rango es inclusivo a la izquierda y exclusivo a la derecha).2k-1
terminará justo antes de la posiciónk
, y el penúltimo carácter se elimina debido a que se ha cambiado a la derecha.Algunos rangos (
k
tok+1
,2k+1
to2k+1
,2k+1
to2k+3
y2k+1
to2k+5
) no tienen sentido físico. Algunos de ellos aparecen como valores intermedios de todos modos, porque es más fácil que agregar controles adicionales para evitarlos.La pila izquierda almacena el número de ediciones necesarias para convertir cada subcadena en una cadena equilibrada. La distancia de edición para el intervalo
(x,y)
se almacena en profundidadx + y(y-1)/2
.Durante el ciclo interno, las entradas se agregan sobre la pila izquierda para indicar qué movimientos son posibles. Estas entradas tienen 5 bytes de longitud. Contando desde la parte superior, los números son
d+1
,y1
,x1
,y2
,x2
, donde los costos de la mudanzad
editar pasos y divide la subcadena en(x1,y1)
y(x2,y2)
.El código
Descripción por venir. Por ahora, aquí está mi copia de trabajo del código. Algunos comentarios pueden ser inconsistentes con la terminología.
fuente
Haskell , 797 bytes
Pruébalo en línea!
fuente