-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwordladder.go
134 lines (107 loc) · 2.86 KB
/
wordladder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package wordladder
import (
"bufio"
"errors"
"fmt"
"regexp"
"strings"
"github.com/gastonsimone/go-dojo/strarray"
)
// WordSet represents a dictionary of words in memory.
type WordSet map[string]bool
// LoadWordDict loads a dictionary of words in memory and returns it as
// a 'WordSet'. 'scanner' is is used as the source of words.
func LoadWordDict(scanner *bufio.Scanner) (WordSet, error) {
if scanner == nil {
return nil, errors.New("nil scanner")
}
dict := make(WordSet)
isWord := regexp.MustCompile(`^\w+$`)
for scanner.Scan() {
word := strings.ToLower(scanner.Text())
if isWord.MatchString(word) {
dict[word] = false
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
return dict, nil
}
// wordLadder represents a backward list of steps until the starting word
type wordLadder struct {
word string
parent *wordLadder // parent word in the ladder (used to get the full conversion path)
}
// queue represents a FIFO queue of words in the ladder
type queue []*wordLadder
// Empty returns true if q has no elements
func (q *queue) empty() bool {
return (len(*q) == 0)
}
// Enqueue adds w at the end of q
func (q *queue) enqueue(w *wordLadder) {
*q = append(*q, w)
}
// Enqueue returns and removes the first element from q
func (q *queue) pop() *wordLadder {
w := (*q)[0]
*q = (*q)[1:]
return w
}
// WordLadder calculates the 'word ladder' between two words.
// Given a 'start' word, an 'end' word, and a dictionary of words 'dict', finds
// the shortest transformation sequence from 'start' to 'end', such that only
// one letter can be changed at a time. Each intermediate word must exist in
// the dictionary.
func WordLadder(start, end string, dict WordSet) []string {
if len(start) != len(end) {
return nil
}
root := new(wordLadder)
root.word = start
visited := make(WordSet)
visited[start] = true
q := new(queue)
q.enqueue(root)
for !q.empty() {
step := q.pop()
word := step.word
chars := strings.Split(word, "")
for i, c := range chars {
for newc := 'a'; newc <= 'z'; newc++ {
newchar := fmt.Sprintf("%c", newc)
if c == newchar {
continue
}
chars[i] = newchar
newword := strings.Join(chars, "")
chars[i] = c
newstep := new(wordLadder)
newstep.word = newword
newstep.parent = step
if newword == end {
return newstep.climbLadder()
}
if _, exists := dict[newword]; exists {
if _, found := visited[newword]; !found {
q.enqueue(newstep)
visited[newword] = true
}
}
}
}
}
return nil
}
// ClimbLadder traverses the words in the ladder, starting from w and returns
// a slice of words, where the first word is the first word in the ladder, and
// the last word is w.word.
func (w *wordLadder) climbLadder() []string {
var words []string
for ; w != nil; w = w.parent {
words = append(words, w.word)
}
strarray.Reverse(words)
return words
}