Crea un alphabeTrie


31

Considera il seguente elenco di parole in ordine alfabetico:

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

Tutte le parole iniziano con be le prime 5 iniziano con bal. Se guardiamo solo le prime 2 parole:

balderdash
ballet

potremmo invece scrivere:

balderdash
  +let

dove ' 'viene usato dove una parola condivide un prefisso con la parola precedente; ad eccezione del '+'carattere che indica l'ULTIMO carattere in cui la seconda parola condivide un prefisso con la parola precedente.

Questa è una sorta di visualizzazione 'trie' : il genitore è ' bal' e ha 2 discendenti: 'derdash'e 'let'.

Con un elenco più lungo, come ad esempio:

balderdash
ballet
brooding

possiamo inoltre utilizzare il carattere pipe '|'per renderlo più chiaro dove finisce il prefisso condiviso, come segue:

balderdash
| +let
+rooding

e l'albero equivalente avrebbe una radice 'b'con due figli: il sottoalbero ha radice 'al'e con i suoi due figli 'derdash'e 'let'; e 'rooding'.

Se applichiamo questa strategia al nostro elenco originale,

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

otteniamo un output simile a:

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

Se due parole consecutive nell'elenco non hanno un prefisso condiviso, non vengono sostituiti caratteri speciali; ad es. per l'elenco:

broom
brood
crude
crumb

vogliamo l'output:

broom
   +d
crude
  +mb

Ingresso

Le parole nell'input saranno costituite solo da caratteri alfanumerici (senza spazi o punteggiatura); questo può essere sotto forma di un elenco di stringhe, una singola stringa o qualsiasi altro approccio ragionevole, purché si specifichi il formato scelto. Non ci saranno due parole consecutive uguali. L'elenco sarà in ordine alfabetico.

Produzione

L'output può contenere spazi bianchi finali per riga o in totale, ma nessuno spazio bianco iniziale. Un elenco di stringhe o simili sarebbe anche accettabile.

Questo è ; il codice più breve in ogni lingua conserva i diritti di vantarsi. Si applicano i consueti divieti contro le scappatoie.

Casi test

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 

Che dire del caso in cui ho la parola balldopo balloon. Quale output dovremmo aspettarci?
Don Mille

@RushabhMehta Immagino che avresti avuto un +under nel primo o, ma non ho scritto la sfida, quindi non ne sono certo.
Theo,

5
@RushabhMehta Le parole sono in ordine alfabetico, quindi non accadrà.
Neil,

@Neil Oh buon punto
Don Mille

2
Le parole nell'input saranno composte solo da caratteri alfanumerici : include davvero cifre o intendevi alfabetico?
Arnauld

Risposte:


11

Retina 0.8.2 , 58 57 byte

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

Provalo online! Il collegamento include un caso di prova. Modifica: salvato 1 byte grazie a @FryAmTheEggman sottolineando che ho trascurato un passaggio da \ba ^reso possibile dal m). Spiegazione:

m)

Attiva per riga ^per l'intero programma.

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

Per ogni parola, prova ad abbinare il più possibile dall'inizio della parola precedente. Cambia la corrispondenza in spazi, tranne l'ultimo carattere, che diventa a +.

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

Sostituisci ripetutamente tutti gli spazi immediatamente sopra +s o |s con |s.


@FryAmTheEggman In effetti, ho aggiunto lo m)specifico per poterlo fare, quindi sono infastidito dal fatto che ho perso un'istanza.
Neil,

Ugh, perché mi preoccupo ancora di rispondere ai commenti se le persone li cancelleranno ...
Neil,

9

JavaScript (ES6), 128 byte

Si aspetta e restituisce un elenco di elenchi di caratteri.

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)))

Provalo online!

Come?

Spazi e +'' possono essere inseriti camminando attraverso la prima fino all'ultima parola in ordine, ma |possono essere inseriti solo a posteriori una volta +identificato. Ciò potrebbe essere ottenuto facendo due passaggi distinti, ma invece salviamo un puntatore a ciascuna voce modificata in a[~y]modo che possa essere successivamente aggiornato di nuovo all'interno dello stesso map()loop.

In teoria, una soluzione più semplice sarebbe quella di scorrere le parole in ordine inverso e invertire l'output anche alla fine del processo. Ma questo è un po 'costoso in JS e non ho trovato un modo per ottenere una versione più breve con questo metodo.

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

guarderesti la mia soluzione, me ne sono inventato io stesso ma mi ricorda la tua soluzione. quindi se è troppo vicino puoi
inviarlo

@DanielIndie Nessun problema. È abbastanza diverso.
Arnauld,


1

Python, 263 260 byte

- 3 byte grazie a Jonathan Frech

Codice:

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)

Provalo online!

Spiegazione:

Questa soluzione crea un trie dalle parole di input e lo analizza ricorsivamente nell'output richiesto. La funzione a prende un trie te una stringa se aggiunge x a t. I tentativi sono implementati come dizionari nidificati. Ogni dizionario rappresenta un nodo nel trie. Ad esempio, il dizionario che rappresenta il trie generato dal primo caso di test è simile al seguente:

{'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 funzione p ricorre attraverso questa struttura e genera la rappresentazione in forma di stringa del trie previsto dalla sfida. La funzione f prende un sacco di stringhe come argomenti, le aggiunge tutte a un trie con a, quindi restituisce il risultato della chiamata di p sul trie.


1
Possibili 252 byte .
Jonathan Frech,

1

C (gcc) , 165 155 byte

Accetta tre argomenti:

  • char** a : una matrice di parole con terminazione null
  • char* m : una matrice della lunghezza di ogni parola
  • int n : il numero di parole nella matrice
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;}

Provalo online!



@Arnauld Certo! Sebbene non sia un ++i<n&j<m[i]&a[i--]comportamento indefinito? Posso fare affidamento su gcc valutandolo da sinistra a destra?
Curtis Bechtel,

È molto probabile che si tratti di un comportamento indefinito. Ma definiamo le lingue in base alla loro implementazione, quindi finché funziona in modo coerente con questa versione di gcc, penso che vada bene.
Arnauld,

1

Perl 6 , 149 144 142 byte

{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("
")}

Provalo online!

Sono sicuro che questo può essere giocato di più, soprattutto perché non sono un esperto di regex. Questo utilizza più o meno lo stesso processo della risposta Retina di Neil .


0

Python 2 , 191 byte

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

Provalo online!


0

Rubino , 118 byte

->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/]&&=?+}}

Provalo online!

Accetta una matrice di stringhe, genera modificando la matrice di input originale sul posto.

Spiegazione

La trasformazione di base della stringa non è troppo complessa, ma per inserire correttamente i tubi verticali, dobbiamo iterare in ordine inverso, e poiché il reversemetodo è abbastanza dettagliato, lo faremo in un modo più complicato. Qui, usiamo mapsolo per eseguire il ciclo, lasciare sola la prima parola e quindi iterare dalla fine usando indici negativi:

->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 +
 }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.