Today we have doublets source code, dictionary source code, queue source code.
Using the same source code as the previous two posts (here and here, described originally here) for the dictionary, the code is a pretty straight forward case of using recursion to do backtracking. Basically, try all of the possible next words one letter different. Whenever you find a dead end, back up and try a different path. Something like this:
; find the path between two words, changing one letters at a time
; use call/cc to bail out when we find an answer
(define (direct-doublet dict src dst)
(call/cc
(λ (exit)
(let ([src (string-upcase src)]
[dst (string-upcase dst)])
; loop down possible solutions
(let loop ([current src] [words (list src)])
; when we find one, bail out entirely
(if (equal? current dst)
(exit (reverse words))
; try all possible values
(for*/list ([i (string-length src)]
[c "ABCDEFGHIJKLMNOPQRSTUVWXYZ"])
(let ([next (string-set current i c)])
(when (and (not (member next words))
(contains? dict next))
(loop next (cons next words))))))))
(exit #f))))
Basically, I’m using a neat trick I last used on the post about 4SUM where call/cc
lets us bail out of the depths of the code as soon as we find a solution. Other than that, it’s a simple matter of using for*
to loop over each position and each character, generating all possible words. Whenever a word is valid (and not one we’ve seen before in this path), keep going. Eventually, we’ll find a solution and can bail out. On the off chance that we don’t, return #f
.
read more...