Mache einen alphabeTrie

31

Betrachten Sie die folgende alphabetisch sortierte Liste von Wörtern:

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

Alle Wörter beginnen mit bund die ersten 5 beginnen mit bal. Wenn wir uns nur die ersten beiden Wörter ansehen:

balderdash
ballet

wir könnten stattdessen schreiben:

balderdash
  +let

wobei das verwendet ' 'wird, wenn ein Wort ein Präfixzeichen mit dem vorherigen Wort teilt; mit Ausnahme des '+'Zeichens, das das letzte Zeichen angibt, bei dem das zweite Wort ein Präfix mit dem vorherigen Wort teilt.

Dies ist eine Art "Trie" -Visualisierung: Das übergeordnete Element ist " bal" und hat 2 Nachkommen: 'derdash'und 'let'.

Mit einer längeren Liste, wie zum Beispiel:

balderdash
ballet
brooding

Wir können zusätzlich das Pipe-Zeichen verwenden '|', um zu verdeutlichen, wo das gemeinsame Präfix endet, wie folgt:

balderdash
| +let
+rooding

und der äquivalente Baum hätte eine Wurzel aus 'b'zwei Kindern: der Teilbaum hätte Wurzel 'al'und und seine zwei Kinder 'derdash'und 'let'; und 'rooding'.

Wenn wir diese Strategie auf unsere ursprüngliche Liste anwenden,

balderdash
ballet
balloonfish
balloonist
ballot
brooding
broom

Wir erhalten eine Ausgabe, die wie folgt aussieht:

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

Wenn zwei aufeinanderfolgende Wörter in der Liste kein gemeinsames Präfix haben, werden keine Sonderzeichen ersetzt. zB für die Liste:

broom
brood
crude
crumb

Wir wollen die Ausgabe:

broom
   +d
crude
  +mb

Eingang

Die Wörter in der Eingabe bestehen nur aus alphanumerischen Zeichen (keine Leerzeichen oder Satzzeichen). Dies kann in Form einer Liste von Zeichenfolgen, einer einzelnen Zeichenfolge oder einer anderen sinnvollen Methode erfolgen, sofern Sie das von Ihnen gewählte Format angeben. Keine zwei aufeinander folgenden Wörter werden gleich sein. Die Liste wird alphabetisch sortiert.

Ausgabe

Ihre Ausgabe kann nachgestellte Leerzeichen pro Zeile oder insgesamt enthalten, jedoch keine führenden Leerzeichen. Eine Liste von Zeichenketten oder ähnlichem wäre ebenfalls akzeptabel.

Das ist ; Der kürzeste Code in jeder Sprache behält sich prahlerische Rechte vor. Es gelten die üblichen Lückenverbote.

Testfälle

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
quelle
Was ist mit dem Fall , in dem ich das Wort habe ballnach balloon. Mit welcher Leistung sollten wir rechnen?
Don Thousand
@RushabhMehta Ich schätze, du hättest nur eine +unter der ersten o, aber ich habe die Herausforderung nicht geschrieben, deshalb bin ich nicht sicher.
Theo
5
@RushabhMehta Die Wörter sind alphabetisch sortiert, dies wird also nicht passieren.
Neil
@ Neil Oh guter Punkt
Don Thousand
2
Die Wörter in der Eingabe bestehen nur aus alphanumerischen Zeichen : Enthält das wirklich Ziffern oder meinten Sie alphabetisch?
Arnauld

Antworten:

11

Retina 0.8.2 , 58 57 Bytes

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

Probieren Sie es online! Link enthält einen Testfall. Bearbeiten: 1 Byte dank @FryAmTheEggman gespeichert, der darauf hinweist, dass ich einen Wechsel von \bzu übersehen ^habe, der durch das ermöglicht wurde m). Erläuterung:

m)

Pro Zeile ^für das gesamte Programm einschalten.

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

Versuchen Sie, für jedes Wort so viele Übereinstimmungen wie möglich vom Anfang des vorherigen Wortes an zu finden. Ändern Sie die Übereinstimmung in Leerzeichen, mit Ausnahme des letzten Zeichens, das zu a wird +.

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

Ersetzen Sie wiederholt alle Leerzeichen unmittelbar über +s oder |s durch |s.

Neil
quelle
@FryAmTheEggman Tatsächlich habe ich das m)speziell hinzugefügt, um das zu können, also ärgere ich mich, dass ich eine Instanz verpasst habe.
Neil
Puh, warum antworte ich überhaupt auf Kommentare, wenn die Leute sie nur löschen wollen ...
Neil
9

JavaScript (ES6), 128 Byte

Erwartet eine Liste mit Zeichenlisten und gibt diese zurück.

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

Probieren Sie es online!

Wie?

Leerzeichen und +'s können eingefügt werden, indem das erste bis zum letzten Wort der Reihe nach |durchgegangen wird. Sie können jedoch erst a posteriori eingefügt werden, wenn a +identifiziert wurde. Dies könnte durch zwei verschiedene Durchläufe erreicht werden. Stattdessen speichern wir einen Zeiger auf jeden geänderten Eintrag, a[~y]damit er später innerhalb derselben map()Schleife erneut aktualisiert werden kann .

Theoretisch wäre es eine einfachere Lösung, die Wörter in umgekehrter Reihenfolge durchzugehen und die Ausgabe am Ende des Prozesses ebenfalls umzukehren. Aber das ist in JS ein bisschen teuer und ich habe mit dieser Methode keinen Weg gefunden, eine kürzere Version zu bekommen.

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
quelle
Würden Sie sich meine Lösung ansehen, ich habe sie mir selbst ausgedacht, aber sie erinnert an Ihre Lösung. Wenn es zu nahe ist, kannst du es als deines einreichen (oder nicht) und es nicht löschen :)
DanielIndie
@ DanielIndie Keine Sorge. Es ist anders genug.
Arnauld
1

Python, 263 260 Bytes

- 3 Bytes dank Jonathan Frech

Code:

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)

Probieren Sie es online!

Erläuterung:

Diese Lösung baut einen Versuch aus den Eingabewörtern auf und analysiert ihn rekursiv in die erforderliche Ausgabe. Die a-Funktion nimmt einen Versuch und einen String s und addiert x zu t. Versuche werden als verschachtelte Wörterbücher implementiert. Jedes Wörterbuch repräsentiert einen Knoten im Trie. Das Wörterbuch, das den vom ersten Testfall generierten Test darstellt, sieht beispielsweise folgendermaßen aus:

{'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': {}}}}}}

Die p-Funktion rekursiert durch diese Struktur und generiert die Zeichenfolgendarstellung des von der Challenge erwarteten Tries. Die Funktion f nimmt eine Reihe von Zeichenfolgen als Argumente, fügt sie alle zu einem Trie mit a hinzu und gibt dann das Ergebnis des Aufrufs von p für den Trie zurück.

Zachary Cotton
quelle
1
Mögliche 252 Bytes .
Jonathan Frech
1

C (gcc) , 165–155 Bytes

Nimmt drei Argumente:

  • char** a : ein Array von nullterminierten Wörtern
  • char* m : Ein Array der Länge jedes Wortes
  • int n : Die Anzahl der Wörter im Array
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;}

Probieren Sie es online!

Curtis Bechtel
quelle
@ Arnauld Natürlich! Obwohl nicht ++i<n&j<m[i]&a[i--]undefiniertes Verhalten? Kann ich mich darauf verlassen, dass gcc es von links nach rechts auswertet?
Curtis Bechtel
Es ist sehr wahrscheinlich undefiniertes Verhalten. Aber wir definieren Sprachen durch ihre Implementierung. Solange es mit dieser Version von gcc konsistent funktioniert, denke ich, ist das in Ordnung.
Arnauld
1

Perl 6 , 149 144 142 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("
")}

Probieren Sie es online!

Ich bin mir sicher, dass man hier mehr Golf spielen kann, zumal ich kein Experte für Regex bin. Dies funktioniert ähnlich wie Neils Retina-Antwort .

Scherzen
quelle
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

Probieren Sie es online!

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

Probieren Sie es online!

Akzeptiert ein Array von Zeichenfolgen und gibt es aus, indem das ursprüngliche Eingabearray direkt geändert wird.

Erläuterung

Die grundlegende String-Transformation ist nicht zu komplex, aber um vertikale Pipes richtig einzufügen, müssen wir in umgekehrter Reihenfolge iterieren, und da die reverseMethode ziemlich ausführlich ist, werden wir es auf eine schwierigere Weise tun. Hier verwenden wir mapnur, um die Schleife auszuführen, das erste Wort in Ruhe zu lassen und dann am Ende mit negativen Indizes zu iterieren:

->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.
quelle