Hacer un abecedario

31

Considere la siguiente lista de palabras ordenadas alfabéticamente:

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

Todas las palabras comienzan con b, y las primeras 5 comienzan con bal. Si solo miramos las primeras 2 palabras:

balderdash
ballet

podríamos escribir en su lugar:

balderdash
  +let

donde ' 'se usa donde una palabra comparte un carácter de prefijo con la palabra anterior; excepto el '+'carácter que indica el ÚLTIMO carácter donde la segunda palabra comparte un prefijo con la palabra anterior.

Esta es una especie de visualización 'trie' : el padre es ' bal' y tiene 2 descendientes: 'derdash'y 'let'.

Con una lista más larga, como:

balderdash
ballet
brooding

también podemos usar el carácter de canalización '|'para aclarar dónde termina el prefijo compartido, de la siguiente manera:

balderdash
| +let
+rooding

y el árbol equivalente tendría una raíz de 'b'tener dos hijos: el subárbol con raíz 'al'y sus dos hijos 'derdash'y 'let'; y 'rooding'.

Si aplicamos esta estrategia a nuestra lista original,

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

obtenemos resultados que se parecen a:

balderdash    
| +let     
|  +oonfish
|   | +ist 
|   +t     
+rooding   
   +m 

Si dos palabras consecutivas en la lista no tienen prefijo compartido, no se sustituyen caracteres especiales; Por ejemplo, para la lista:

broom
brood
crude
crumb

queremos la salida:

broom
   +d
crude
  +mb

Entrada

Las palabras en la entrada estarán compuestas únicamente de caracteres alfanuméricos (sin espacios ni signos de puntuación); Esto puede ser en forma de una lista de cadenas, una sola cadena o cualquier otro enfoque razonable, siempre que especifique el formato elegido. No hay dos palabras consecutivas iguales. La lista se ordenará alfabéticamente.

Salida

Su salida puede contener espacios en blanco finales por línea o en total, pero no espacios en blanco iniciales. Una lista de cadenas o similar también sería aceptable.

Este es el ; El código más corto en cada idioma conserva los derechos de fanfarronear. Se aplican las prohibiciones habituales contra las lagunas.

Casos de prueba

Input:
apogee
apology
app
apple
applique
apply
apt

Output:
apogee     
 |+logy    
 +p        
 |+le      
 | +ique   
 | +y      
 +t        

Input:
balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom
donald
donatella
donna
dont
dumb

Output:
balderdash 
| +let     
|  +oonfish
|   | +ist 
|   +t     
+rooding   
   +m      
donald     
| |+tella  
| +na      
| +t       
+umb 
Chas Brown
fuente
¿Qué pasa con el caso donde tengo la palabra balldespués balloon? ¿Qué salida debemos esperar?
Don Thousand
@RushabhMehta Supongo que solo tendrías un +debajo del primero o, pero no escribí el desafío, así que no estoy seguro.
Theo
55
@RushabhMehta Las palabras están ordenadas alfabéticamente, por lo que esto no sucederá.
Neil
@Neil Oh buen punto
Don Thousand
2
Las palabras en la entrada estarán compuestas únicamente de caracteres alfanuméricos : ¿eso realmente incluye dígitos o quiso decir alfabético?
Arnauld

Respuestas:

11

Retina 0.8.2 , 58 57 bytes

^((.*).)(?<=\b\1.*¶\1)
$.2$* +
m)+`^(.*) (.*¶\1[+|])
$1|$2

Pruébalo en línea! El enlace incluye un caso de prueba. Editar: Guardado 1 byte gracias a @FryAmTheEggman señalando que pasé por alto un cambio de \bal ^hecho posible por el m). Explicación:

m)

Active por línea ^para todo el programa.

^((.*).)(?<=^\1.*¶\1)
$.2$* +

Para cada palabra, intente hacer coincidir tanto como sea posible desde el comienzo de la palabra anterior. Cambia la coincidencia a espacios, excepto el último carácter, que se convierte en a +.

+`^(.*) (.*¶\1[+|])
$1|$2

Reemplace repetidamente todos los espacios inmediatamente superiores a +so |s con |s.

Neil
fuente
@FryAmTheEggman De hecho, agregué m)específicamente para poder hacer eso, así que me molesta que me haya perdido una instancia.
Neil
Ugh, ¿por qué me molesto en responder a los comentarios si las personas simplemente los van a eliminar ...
Neil
9

JavaScript (ES6), 128 bytes

Espera y devuelve una lista de listas de caracteres.

a=>a.map((w,y)=>a[~y]=w.map(m=(c,x)=>(p=a[y-1]||0,m|=c!=p[x])?c:p[x+1]==w[x+1]?' ':(g=y=>a[y][x]<1?g(y+1,a[y][x]='|'):'+')(-y)))

Pruébalo en línea!

¿Cómo?

Los espacios y +'s se pueden insertar caminando a través de la primera a la última palabra en orden, pero |solo se pueden insertar a posteriori una vez que +se ha identificado. Esto podría lograrse haciendo dos pases distintos, pero en su lugar guardamos un puntero en cada entrada modificada a[~y]para que luego pueda actualizarse nuevamente dentro del mismo map()ciclo.

En teoría, una solución más simple sería recorrer las palabras en orden inverso y también invertir la salida al final del proceso. Pero esto es un poco costoso en JS y no encontré una manera de obtener una versión más corta con este método.

a =>                           // a[] = input array
  a.map((w, y) =>              // for each word w at position y in a[]:
    a[~y] =                    //   save a pointer to the current entry in a[~y]
    w.map(m =                  //   initialize m to a non-numeric value
      (c, x) => (              //   for each character c at position x in w:
        p = a[y - 1] || 0,     //     p = previous word or a dummy object
        m |= c != p[x]         //     set m = 1 as soon as w differs from p at this position
      ) ?                      //     if w is no longer equal to p:
        c                      //       append c
      :                        //     else:
        p[x + 1] == w[x + 1] ? //       if the next characters are still matching:
          ' '                  //         append a space
        : (                    //       else:
            g = y =>           //         g() = recursive function to insert pipes
            a[y][x] < 1 ?      //           if a[y][x] is a space:
              g(               //             do a recursive call to g()
                y + 1,         //               with y + 1
                a[y][x] = '|'  //               and overwrite a[y][x] with a pipe
              )                //             end of recursive call
            :                  //           else:
              '+'              //             make the whole recursion chain return a '+'
                               //             which will be appended in the current entry
          )(-y)                //         initial call to g() with -y (this is ~y + 1)
    )                          //   end of map() over the characters
  )                            // end of map() over the words
Arnauld
fuente
¿mirarías mi solución? Se me ocurrió, pero me recuerda a tu solución. así que si está demasiado cerca, puedes enviarlo como tuyo (o no) y borrarlo :)
DanielIndie
@DanielIndie No te preocupes. Es lo suficientemente diferente.
Arnauld
2

JavaScript (Node.js) , 125 122 118 117 bytes

a=>a.map((w,i)=>a[i]=w.map((T,k)=>+i&&l[k]==T?l[-~k]<w[-~k]?(g=_=>a[--i][k]<"+"?g(a[i][k]="|"):"+")():" ":(i=l=w,T)))

Pruébalo en línea!

  • gracias a @Arnauld por las pruebas de tio :)
DanielIndie
fuente
1

Python, 263 260 bytes

- 3 bytes gracias a Jonathan Frech

Código:

p=lambda t,f,g:"\n".join([(f[:-1]+"+"if(a!=min(t))*g else"")+a+p(t[a],(f+" "if len(t[a])>1or a==max(t)else f[:-1]+"| "),1)for a in t])if t else""
def a(t,x):
 if x:c=x[0];t[c]=c in t and t[c]or{};a(t[c],x[1:])
def f(*s):t={};[a(t,i)for i in s];return p(t,"",0)

Pruébalo en línea!

Explicación:

Esta solución crea un trie a partir de las palabras de entrada y lo analiza de forma recursiva en la salida requerida. La función a toma un trie t y una cadena s y agrega x a t. Las pruebas se implementan como diccionarios anidados. Cada diccionario representa un nodo en el trie. Por ejemplo, el diccionario que representa el trie generado por el primer caso de prueba se ve así:

{'b': {'a': {'l': {'d': {'e': {'r': {'d': {'a': {'s': {'h': {}}}}}}}, 'l': {'e': {'t': {}}, 'o': {'o': {'n': {'f': {'i': {'s': {'h': {}}}}, 'i': {'s': {'t': {}}}}}, 't': {}}}}}, 'r': {'o': {'o': {'d': {'i': {'n': {'g': {}}}}, 'm': {}}}}}}

La función p se repite a través de esta estructura y genera la representación en cadena del trie esperado por el desafío. La función f toma un montón de cadenas como argumentos, las agrega todas a un trie con a, luego devuelve el resultado de llamar a p en el trie.

Algodón Zachary
fuente
1
Posibles 252 bytes .
Jonathan Frech
1

C (gcc) , 165155 bytes

Toma tres argumentos:

  • char** a : una matriz de palabras terminadas en nulo
  • char* m : una matriz de la longitud de cada palabra
  • int n : el número de palabras en la matriz
f(a,m,n,i,j)char**a,*m;{for(i=n;--i;)for(j=0;j<m[i]&j<m[i-1]&a[i][j]==a[i-1][j];j++)a[i][j]=a[i][j+1]^a[i-1][j+1]?43:++i<n&j<m[i]&a[i--][j]%81==43?124:32;}

Pruébalo en línea!

Curtis Bechtel
fuente
@Arnauld ¡Por supuesto! Aunque no es ++i<n&j<m[i]&a[i--]un comportamiento indefinido? ¿Puedo confiar en que gcc lo evalúe de izquierda a derecha?
Curtis Bechtel
Es muy probable que sea un comportamiento indefinido. Pero definimos los idiomas por su implementación, por lo que siempre que funcione de manera consistente con esta versión de gcc, creo que está bien.
Arnauld
1

Perl 6 , 149144142 bytes

{1 while s/(\n.*)\s(.*)$0(\+|\|)/$0|$1$0$2/;$_}o{$=({.[1].subst(/^(.+)<?{.[0].index($0)eq 0}>/,{' 'x$0.ords-1~'+'})}for '',|$_ Z$_).join("
")}

Pruébalo en línea!

Estoy seguro de que esto se puede jugar más, especialmente porque no soy un experto en expresiones regulares. Esto utiliza el mismo proceso que la respuesta de Retina de Neil .

Jo King
fuente
0

Python 2 , 191 bytes

def f(w,r=['']):
 for b,c in zip(w[1:],w)[::-1]:
	s='';d=0
	for x,y,z in zip(r[0]+b,b,c+b):t=s[-1:];s=s[:-1]+[['+'*(s>'')+y,t+' |'[x in'+|']][y==z],t+y][d];d=d|(y!=z)
	r=[s]+r
 return[w[0]]+r

Pruébalo en línea!

Chas Brown
fuente
0

Ruby , 118 bytes

->a{i=1;a.map{s="";a[i+=j=-1].chars{|c|a[i][j+=1]=i<0&&a[i-1][/^#{s+=c}/]?a[i+1][j]=~/[|+]/??|:?\s:c}[/[| ]\b/]&&=?+}}

Pruébalo en línea!

Acepta una matriz de cadenas, salidas modificando la matriz de entrada original en el lugar.

Explicación

La transformación de cadena básica no es demasiado compleja, pero para insertar tuberías verticales correctamente, necesitamos iterar en orden inverso, y dado que el reversemétodo es bastante detallado, lo haremos de una manera más complicada. Aquí, usamos mapsolo para ejecutar el ciclo, dejamos la primera palabra sola y luego iteramos desde el final usando índices negativos:

->a{
 i=1;                   #Initialize word indexer
 a.map{                 #Loop
  s="";                 #Initialize lookup string
  a[i+=j=-1]            #Initialize char indexer and decrement i
  .chars{|c|            #Loop through each char c of current word
   a[i][j+=1]=          #Mofify current word at position j 
    i<0&&               #If it's not the first word and
    a[i-1][/^#{s+=c}/]? #Word above matches current one from start to j
     a[i+1][j]=~/[|+]/? #Then if char below is | or +
      ?|:?\s:c          #Then set current char to | Else to Space Else leave as is
  }[/[| ]\b/]&&=?+      #Finally, replace Space or | at word boundary with +
 }
}
Kirill L.
fuente