Accesibilidad de Word Changer

13

Word Changer es un juego en el que intentas convertir una palabra en otra a través de ediciones de un solo carácter, y cada paso es su propia palabra. Para este desafío, las ediciones pueden ser reemplazos, inserciones o eliminaciones. Por ejemplo, WINNER → LOSER se puede hacer con esta ruta (puede haber otras):

WINNER
DINNER
DINER
DINE
LINE
LONE
LOSE
LOSER

Dicho de otra manera, debe poder alcanzar una palabra de la otra yendo solo a través de otras palabras a una distancia de Levenshtein de 1 cada vez.

Codificación

Se le proporcionará una lista de palabras y dos palabras y debe generar una ruta válida de una palabra a la otra si existe una ruta o un valor constante distinto o un comportamiento constante si no existe una ruta.

  • Puede suponer que las palabras de entrada están ambas en la lista de palabras
  • La lista de palabras se puede acceder a través de cualquier formato plano conveniente.
    • Las listas, los conjuntos, los intentos, las cadenas separadas por espacios y los archivos separados por líneas son todos válidos (por ejemplo), pero un gráfico precalculado de adyacencia de Levenshtein no lo es.
  • La ruta de salida debe incluir ambas palabras de entrada, pero lo que comienza y termina no importa.
  • Si no se encuentra ninguna ruta, puede generar una constante específica, un valor falso, una lista vacía, lanzar una excepción, salir con un código distinto de cero o cualquier otro comportamiento que ocurra en un tiempo finito.
  • La ruta no necesita ser óptima y no se requiere qué ruta se debe tomar
  • La complejidad computacional no importa, sin embargo, se debe garantizar que su programa termine en un tiempo limitado. (incluso si fuera más allá de la muerte por calor del universo)
  • Puede suponer que todas las palabras están completamente compuestas de letras en el mismo caso

Ejemplos de casos de prueba

  • GATO → PERRO; [GATO, PERRO, COG, COT, RANA, GROG, BOG]
    • CAT, COT, COG, PERRO
  • BAÑO → DUCHA; [BAÑO, DUCHA, HATH, SOMBRERO, BAT, SAT, SAW, SOW, SHOW, CÓMO]
    • No se encontró ruta
  • BREAK → FIX; [BREAK, FIX, BEAK, BREAD, READ, BEAD, RED, BED, BAD, BID, FAD, FAX]
    • BREAK, BREAD, BEAD, BAD, FAD, FAX, FIX
  • CONSTRUIR → DESTRUIR; [CONSTRUIR, DESTRUIR, CONSTRUIR, CULPAR, GUILD, DORAR, GILL, BILL, DILL, FILL, DESTRUCT, ESTRUCTURA, CONSTRUCT]
    • No se encontró ruta
  • TARJETA → TABLERO; [TARJETA, TABLERO, BARDO]
    • TARJETA, BARDO, TABLERO
  • DEMONIO → ÁNGEL; [DEMONIO, ÁNGEL]
    • No se encontró ruta
  • ÚLTIMO → PASADO; [ÚLTIMO, PASADO, BLAST, CAST, BLACK, GHOST, POST, BOAST]
    • ULTIMO, PASADO
  • INSERTAR → ELIMINAR; Esta lista de palabras
    • INSERTAR, INVERTIR, INVENTAR, INBENTAR, DESMONTAJE, DESMONTAJE, DESVINCULAR, DESVINCULAR, DESCUBRIR, INKING, IRKING, DIRKING, DARKING, DARLING, ARLING, AILING, SIRING, SERING, SERINE, NERINE, NERITE, CERITE, CERATE, DERATE, DELATE, ELIMINAR
Carne de res
fuente
1
¿Podemos generar una lista de rutas válidas o debería ser una ruta?
Emigna
@Emigna cualquier ruta servirá. Como mencioné "La ruta no necesita ser óptima"
Beefster
¿Necesitamos incluir la palabra inicial y final en la salida? ¡Las rutas siempre comenzarán y terminarán igual!
Urna mágica del pulpo
1
@MagicOctopusUrn "La ruta de salida debe incluir ambas palabras de entrada, pero lo que comienza y termina no importa".
Beefster

Respuestas:

5

05AB1E , 23 21 20 bytes

Imprime una lista de rutas válidas.
Guardado 2 bytes gracias a Kevin Cruijssen .

怜€`ʒü.LP}ʒ¬²Qsθ³Q*

Pruébalo en línea!

Emigna
fuente
Puede guardar 2 bytes cambiando Dævyœ«}a 怜€` . (No estoy seguro de por qué ambos mapas por separado funcionan bien, pero æεœ`}no por cierto, pero de todos modos es el mismo conteo de bytes.)
Kevin Cruijssen
Lástima que el producto []sea ​​en 1lugar de 0(sin embargo, no es demasiado sorprendente) o que una verificación igual con una lista vacía aparentemente dé como resultado una lista vacía en lugar de 0(esta la veo como un error ...). De lo contrario, podría haber combinado el filtro y find_first para guardar otro byte:怜€`.Δü.LPy¬²Qsθ³QP
Kevin Cruijssen
@KevinCruijssen: ¡Gracias! No estoy seguro de por qué no pensé en usar . Creo que la verificación igual da como resultado una lista vacía debido a la vectorización. Tal vez debería haber un caso especial para la lista vacía, pero tal vez eso sería inesperado en otros casos.
Emigna
1
Hace algo así para 17: ¡ Pruébalo en línea!
Urna mágica del pulpo
1
@MagicOctopusUrn: Desafortunadamente, necesitamos incluir todas las palabras de la ruta en la salida.
Emigna
4

JavaScript (V8) ,  177  176 bytes

Toma entrada como (target)(source, list). Imprime todas las rutas posibles. O no imprime nada si no hay solución.

t=>F=(s,l,p=[],d)=>s==t?print(p):l.map((S,i)=>(g=(m,n)=>m*n?1+Math.min(g(m-1,n),g(m,--n),g(--m,n)-(S[m]==s[n])):m+n)(S.length,s.length)^d||F(S,L=[...l],[...p,L.splice(i,1)],1))

Pruébalo en línea!

Comentado

t =>                            // t = target string
F = (                           // F is a recursive function taking:
  s,                            //   s = source string
  l,                            //   l[] = list of words
  p = [],                       //   p[] = path
  d                             //   d = expected Levenshtein distance between s and the
) =>                            //       next word (initially undefined, so coerced to 0)
  s == t ?                      // if s is equal to t:
    print(p)                    //   stop recursion and print the path
  :                             // else:
    l.map((S, i) =>             //   for each word S at index i in l[]:
      ( g =                     //     g = recursive function computing the Levenshtein
        (m, n) =>               //         distance between S and s
        m * n ?                 //       if both m and n are not equal to 0:
          1 + Math.min(         //         add 1 to the result + the minimum of:
            g(m - 1, n),        //           g(m - 1, n)
            g(m, --n),          //           g(m, n - 1)
            g(--m, n) -         //           g(m - 1, n - 1), minus 1 if ...
            (S[m] == s[n])      //           ... S[m - 1] is equal to s[n - 1]
          )                     //         end of Math.min()
        :                       //       else:
          m + n                 //         return either m or n
      )(S.length, s.length)     //     initial call to g with m = S.length, n = s.length
      ^ d ||                    //     unless the distance is not equal to d,
      F(                        //     do a recursive call to F with:
        S,                      //       the new source string S
        L = [...l],             //       a copy L[] of l[]
        [...p, L.splice(i, 1)], //       the updated path (removes S from L[])
        1                       //       an expected distance of 1
      )                         //     end of recursive call
    )                           //   end of map()
Arnauld
fuente
3

Python 2 , 155 bytes

f=lambda a,b,W,r=[]:a==b and r+[a]or reduce(lambda q,w:q or any({a,a[:i]+a[i+1:]}&{w,w[:i]+w[i+1:]}for i in range(len(a+w)))and f(w,b,W-{a},r+[a]),W-{a},0)

Pruébalo en línea!

Toma dos palabras y un conjunto de palabras como entrada; devuelve una ruta (no óptima) si existe una como una lista de cadenas; de lo contrario, devuelve False.

Este fragmento:

any({a,a[:i]+a[i+1:]}&{w,w[:i]+w[i+1:]}for i in range(len(a+w)))

es Truesi y sólo si a==wo atiene Levenshtein distancia de 1partir w.

Chas Brown
fuente
2

Python 2 , 163 bytes

Si se encuentra una ruta, se envía a stderr y el programa sale con el código de salida 1.
Si no hay ruta, no hay salida y el programa termina con el código de salida 0.

s,e,d=input();r=[[s]]
for x in r:t=x[-1];t==e>exit(x);r+=[x+[w]for w in d-set(x)for a,b in(t,w),(w,t)for i in range(len(b)*2)if a==b[:i/2]+a[i/2:][:i%2]+b[i/2+1:]]

Pruébalo en línea!

ovs
fuente
1

Python 3 , 217 214 212 201 bytes

-11 bytes gracias a una pista de xnor

d=lambda a,b:min(d(a[1:],b[1:])+(a[0]!=b[0]),d(a[1:],b)+1,d(a,b[1:])+1)if b>""<a else len(a+b)
def g(a,b,l,p=[]):
	if a==b:yield[a]+p
	for c in(a!=b)*l:
		if(c in p)+d(a,c)==1:yield from g(c,b,l,[a]+p)

Pruébalo en línea!

movatica
fuente
0

Jalea , 38 bytes

⁵ḟ,€0ị$ṭ¹-Ƥ$€e€/ẸƊƇḢ€
Wṭ@ⱮÇßƊe@⁴oṆƲ?€Ẏ

Pruébalo en línea!

Un programa completo que acepta tres argumentos. La primera es la palabra inicial y se suministra como [["START"]]. El segundo argumento es la palabra final, suministrada como "END". El tercer argumento es la lista de palabras, suministrada como palabras citadas, separadas por comas.

El programa devuelve una lista de listas, y cada lista representa una ruta válida desde el principio hasta el final. Si no hay una ruta válida, la respuesta es una lista vacía.

En el enlace TIO, hay texto de pie de página para mostrar el resultado muy bien con cada palabra separada por espacios y cada lista de palabras separadas por nuevas líneas. Si se prefiere una representación de lista subyacente impresa, esto podría hacerse como ÇŒṘ.

A diferencia de 05ABIE, no hay una distancia incorporada para Levenshtein, por lo que este programa compara outfixes con un solo carácter que falta, algo similar a la solución de @ ChasBrown , aunque con un toque de gelatina.

Explicación

Enlace auxiliar: enlace monádico que toma una lista de palabras y devuelve una lista de posibles listas extendidas, o una lista vacía si no es posible una extensión adicional

⁵ḟ                      | Filter the word list to remove words already used
  ,€0ị$                 | Pair each word with the last word in the current path
                  ƊƇ    | Filter these pairs such that
              e€/Ẹ      |   there exists any
       ṭ¹-Ƥ$€           |   match between the original words or any outfix with a single character removed
                    Ḣ€  | Take the first word of each of these pairs (i.e. the possible extensions of the route)

Enlace principal

              €         | For each of the current paths
            Ʋ?          | If:
       e@⁴              |   The path contains the end word
          oṆ            |   Or the path is empty (i.e. could not be extended)
W                       | Return the path wrapped in a list (which will be stripped by the final Ẏ)
 ṭ@ⱮÇ                   | Otherwise, look for possible extensions using the helper link, and add onto the end of the path
     ßƊ                 | And then feed all of these paths back through this link
               Ẏ        | Strip back one layer of lists (needed because each recursion nests the path one list deeper)
Nick Kennedy
fuente
0

Swift 4.2 / Xcode 10.2.1 , 387 bytes

func d(l:String,m:String)->Bool{return (0..<l.count).contains{var c=l;c.remove(at:c.index(c.startIndex,offsetBy:$0));return c==m}};func f(r:[String])->[String]{if b==r.last!{return r};return w.lazy.map{!r.contains($0)&&(d(l:r.last!,m:$0)||d(l:$0,m:r.last!)||(r.last!.count==$0.count&&zip(r.last!,$0).filter{$0 != $1}.count==1)) ? f(r:r+[$0]):[]}.first{!$0.isEmpty} ?? []};return f(r:[a])

Pruébalo en línea!

Roman Podymov
fuente