-
Notifications
You must be signed in to change notification settings - Fork 0
/
WordChessImp.java
199 lines (180 loc) · 6.22 KB
/
WordChessImp.java
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
/**
* A class that implements the fractional and discrete Knapsack algorithms
* by implementing the WordChess interface.
* @author Bruce How (22242664)
*/
public class WordChessImp implements WordChess {
/**
* Private subclass which stores each word and the parent Node
* leading to the word.
*/
private class Node {
String word;
Node parent;
int depth;
public Node(String word, Node parent, int depth) {
this.word = word;
this.parent = parent;
this.depth = depth;
}
}
/**
* Private subclass to represent a TrieNode used in the Trie
* data structure.
*/
private class TrieNode {
char ch;
ArrayList<TrieNode> children;
boolean end;
public TrieNode(char ch){
this.ch = ch;
this.children = new ArrayList<TrieNode>();
this.end = false;
}
}
/**
* Private subclass to represent the Trie data structure.
* The Trie data structure is a efficient data stucture
* that can be used to search and insert Strings. It is
* used to contain the valid words from a dictionary in
* this case. Inserting and searching is done in O(n)
* where n is the length of the word to search.
*/
private class Trie {
TrieNode root;
public Trie() {
root = new TrieNode('*');
}
/**
* Performs a search to identify all words that differ
* by only one character at most. The complexity of this
* method is O(n * alphabet_size) where n is the length
* of the word to search.
*/
public ArrayList<String> getNeighbours(String word) {
ArrayList<String> res = new ArrayList<String>();
for (int i = 0; i < word.length(); i++) {
String prepend = word.substring(0, i);
TrieNode node = root;
int j = 0;
boolean exit = false;
while (j < i) {
boolean found = false;
for (TrieNode child : node.children) {
if (child.ch == word.charAt(j)) {
found = true;
node = child;
}
}
if (!found) {
exit = true;
}
j++;
}
if (exit) {
continue;
}
for (TrieNode varChild : node.children) {
if (varChild.ch == word.charAt(i)) {
continue;
}
j = i + 1;
TrieNode nestedNode = varChild;
boolean exitEarly = false;
while (j < word.length()) {
boolean found = false;
for (TrieNode child : nestedNode.children) {
if (child.ch == word.charAt(j)) {
found = true;
nestedNode = child;
}
}
if (!found) {
exitEarly = true;
break;
}
j++;
}
if (exitEarly) {
continue;
}
if (nestedNode.end) {
if (i == word.length() -1) {
res.add(prepend + varChild.ch);
} else {
res.add(prepend + varChild.ch + word.substring(i+1));
}
}
}
}
return res;
}
/**
* Adds a word to the Trie. Used to store all
* valid words from a dictionary. A valid word
* is a word that has the same length as the
* starting and ending word.
* @param word The word to add to the Trie
*/
public void addWord(String word) {
TrieNode node = root;
for (int i = 0; i < word.length(); i++) {
boolean found = false;
for (TrieNode child : node.children) {
if (child.ch == word.charAt(i)) {
found = true;
node = child;
break;
}
}
if (!found) {
TrieNode newNode = new TrieNode(word.charAt(i));
node.children.add(newNode);
node = newNode;
}
}
node.end = true;
}
}
@Override
public String[] findPath(String[] dictionary, String startWord, String endWord) {
int length = startWord.length();
Trie trie = new Trie();
for (String word : dictionary) {
if (word.length() == length) {
trie.addWord(word);
}
}
Deque<Node> queue = new LinkedList<>();
HashSet<String> explored = new HashSet<>();
Node current = null;
Boolean foundWord = false;
queue.addLast(new Node(startWord, null, 1));
explored.add(startWord);
while(!foundWord) {
current = queue.removeFirst();
ArrayList<String> words = trie.getNeighbours(current.word);
for (String word : words) {
if (!explored.contains(word)) {
if (word.equals(endWord)) {
foundWord = true;
break;
}
queue.addLast(new Node(word, current, current.depth+1));
explored.add(word);
}
}
}
String[] result = new String[current.depth+1];
result[result.length-1] = endWord;
for (int i = result.length-1; i > 0; i--) {
result[i-1] = current.word;
current = current.parent;
}
return result;
}
}