Erreichbarkeit des Wortwechslers

13

Word Changer ist ein Spiel, bei dem Sie versuchen, ein Wort durch Bearbeiten einzelner Zeichen in ein anderes zu verwandeln, wobei jeder Schritt ein eigenes Wort ist. Für diese Herausforderung können Änderungen Ersetzungen, Einfügungen oder Löschungen sein. Zum Beispiel kann WINNER → LOSER mit dieser Route durchgeführt werden (es kann auch andere geben):

WINNER
DINNER
DINER
DINE
LINE
LONE
LOSE
LOSER

Anders ausgedrückt, Sie müssen in der Lage sein, ein Wort von dem anderen zu erreichen, indem Sie nur durch andere Wörter mit einem Levenshtein-Abstand von jeweils 1 gehen.

Codierung

Sie erhalten eine Wortliste und zwei Wörter und müssen eine gültige Route von einem Wort zum anderen ausgeben, wenn eine Route existiert, oder einen bestimmten konstanten Wert oder ein konsistentes Verhalten, wenn keine Route existiert.

  • Sie können davon ausgehen, dass sich beide Eingabewörter in der Wortliste befinden
  • Die Wortliste kann über jedes beliebige flache Format aufgenommen werden.
    • Listen, Mengen, Versuche, durch Leerzeichen getrennte Zeichenfolgen und durch Zeilen getrennte Dateien sind alle gültig (z. B.), ein vorberechnetes Diagramm der Levenshtein-Adjazenz jedoch nicht.
  • Die Ausgaberoute sollte beide Eingabewörter enthalten, aber was beginnt und endet, spielt keine Rolle.
  • Wenn keine Route gefunden wird, können Sie eine bestimmte Konstante, einen falschen Wert, eine leere Liste, eine Ausnahme ausgeben, mit einem Code ungleich Null beenden oder jedes andere Verhalten, das in endlicher Zeit auftritt.
  • Die Route muss nicht optimal sein und es gibt keine Anforderung, welche Route genommen werden soll
  • Die Komplexität der Berechnungen spielt keine Rolle, es muss jedoch nachweislich gewährleistet sein, dass Ihr Programm in einer begrenzten Zeitspanne endet. (Auch wenn es über den Hitzetod des Universums hinaus laufen würde)
  • Sie können davon ausgehen, dass alle Wörter im selben Fall vollständig aus Buchstaben bestehen

Beispiel Testfälle

  • CAT → HUND; [CAT, HUND, COG, COT, FROSCH, GROG, BOG]
    • CAT, COT, COG, DOG
  • BAD → DUSCHE; [BAD, DUSCHE, HAT, HAT, BAT, SAT, SÄGE, SÄGE, SHOW, WIE]
    • Keine Route gefunden
  • BREAK → FIX; [BREAK, FIX, BEAK, BROT, READ, BEAD, ROT, BETT, SCHLECHT, BIETEN, FAD, FAX]
    • BREAK, BROT, BEAD, BAD, FAD, FAX, FIX
  • BAUEN → ZERSTÖREN; [BAUEN, ZERSTÖREN, BAUEN, SCHULDEN, GILDEN, GILDEN, KÜHLEN, RECHNEN, DILLEN, FÜLLEN, ZERSTÖREN, STRUKTURIEREN, BAUEN]
    • Keine Route gefunden
  • CARD → BOARD; [KARTE, BRETT, BARD]
    • KARTE, BARD, BOARD
  • DEMON → ENGEL; [DEMON, ANGEL]
    • Keine Route gefunden
  • LAST → PAST; [LAST, PAST, BLAST, CAST, SCHWARZ, GEIST, POST, BOAST]
    • LETZT, VERGANGEN
  • INSERT → DELETE; Diese Wortliste
    • INSERT, INVERT, INVENT, INBENT, UNBENT, UNBEND, UNBIND, UNKIND, UNKING, INKING, IRKING, DIRKING, DUNKEL, DARLING, ARLING, AILING, SIRING, SERING, SERIN, NERIN, NERIT, CERIT, CERATE LÖSCHEN
Beefster
quelle
Related , Related
Beefster
1
Können wir eine Liste gültiger Routen ausgeben oder sollte es eine Route sein?
Emigna
@Emigna jede Route reicht. Wie ich schon
sagte
Müssen wir das Start- und Endwort in die Ausgabe aufnehmen? Die Routen beginnen und enden immer gleich!
Magic Octopus Urn
1
@MagicOctopusUrn "Die Ausgaberoute sollte beide Eingabewörter enthalten, aber was beginnt und endet, spielt keine Rolle."
Beefster

Antworten:

5

05AB1E , 23 21 20 Bytes

Druckt eine Liste der gültigen Routen.
2 Bytes dank Kevin Cruijssen gespeichert .

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

Probieren Sie es online!

Emigna
quelle
Sie können durch Ändern 2 Bytes speichern Dævyœ«}zu 怜€` . (Nicht sicher, warum beide Karten separat mit funktionieren, aber æεœ`}nicht übrigens, aber es ist die gleiche
Byteanzahl
Schade , dass das Produkt aus []ist 1statt 0(nicht allzu überraschend, obwohl) oder dass eine gleiche Prüfung mit einer leeren Liste scheinbar Ergebnisse in einer leeren Liste statt 0(diese ich als Fehler sehen ..) .. Ansonsten Sie kombiniert haben könnten der Filter und find_first, um ein weiteres Byte zu speichern:怜€`.Δü.LPy¬²Qsθ³QP
Kevin Cruijssen
@ KevinCruijssen: Danke! Ich bin mir nicht sicher, warum ich nicht daran gedacht habe . Ich denke, die Gleichheitsprüfung führt aufgrund der Vektorisierung zu einer leeren Liste. Vielleicht sollte es einen Sonderfall für die leere Liste geben, aber vielleicht wäre das in anderen Fällen unerwartet.
Emigna
1
Funktioniert so etwas für 17: Online ausprobieren!
Magic Octopus Urn
1
@MagicOctopusUrn: Leider müssen wir alle Wörter des Pfades in die Ausgabe aufnehmen.
Emigna
4

JavaScript (V8) ,  177 bis  176 Byte

Übernimmt die Eingabe als (target)(source, list). Druckt alle möglichen Routen. Oder druckt nichts, wenn es keine Lösung gibt.

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

Probieren Sie es online!

Kommentiert

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

Probieren Sie es online!

Nimmt zwei Wörter und eine Reihe von Wörtern als Eingabe; Gibt eine (nicht optimale) Route zurück, wenn eine als Liste von Zeichenfolgen vorhanden ist, andernfalls wird False zurückgegeben.

Dieses Fragment:

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

ist Truegenau dann, wenn a==woder ahat Levenshtein Abstand 1von w.

Chas Brown
quelle
2

Python 2 , 163 Bytes

Wenn eine Route gefunden wurde, wird sie an stderr
ausgegeben und das Programm wird mit dem Beendigungscode 1 beendet. Wenn keine Route vorhanden ist, erfolgt keine Ausgabe und das Programm wird mit dem Beendigungscode 0 beendet.

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:]]

Probieren Sie es online!

ovs
quelle
1

Python 3 , 217 214 212 201 Bytes

-11 Bytes Danke an einen Hinweis von 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)

Probieren Sie es online!

movatica
quelle
0

Jelly , 38 Bytes

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

Probieren Sie es online!

Ein vollständiges Programm, das drei Argumente akzeptiert. Das erste ist das Startwort und wird als geliefert [["START"]]. Das zweite Argument ist das letzte Wort, geliefert als "END". Das dritte Argument ist die Wortliste, die als zitierte, durch Kommas getrennte Wörter geliefert wird.

Das Programm gibt eine Liste von Listen zurück, wobei jede Liste einen gültigen Pfad vom Anfang bis zum Ende darstellt. Wenn es keine gültige Route gibt, ist die Antwort eine leere Liste.

In der TIO-Verknüpfung gibt es einen Fußzeilentext, der das Ergebnis gut anzeigt, wobei jedes Wort durch Leerzeichen und jede Liste von Wörtern durch Zeilenumbrüche getrennt ist. Wenn ein Ausdruck der zugrunde liegenden Listendarstellung bevorzugt wird, kann dies wie folgt erfolgen ÇŒṘ.

Im Gegensatz zu 05ABIE gibt es für Levenshtein-Entfernungen keine integrierte Funktion. Daher vergleicht dieses Programm Outfixes mit einem einzelnen fehlenden Zeichen, ähnlich wie bei der @ ChasBrown-Lösung , jedoch mit einem Jelly-Twist.

Erläuterung

Hilfslink: monadischer Link, der eine Liste von Wörtern aufnimmt und eine Liste möglicher erweiterter Listen zurückgibt, oder eine leere Liste, wenn keine weitere Erweiterung möglich ist

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

Hauptlink

              €         | 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
quelle
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])

Probieren Sie es online!

Roman Podymov
quelle