Skip to content

Commit 16cf108

Browse files
authored
Merge pull request ByteByteGoHq#14 from marttp/kotlin-trie
Kotlin Chapter 12: Tries
2 parents f1705c5 + 4c10a7e commit 16cf108

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

kotlin/Tries/DesignATrie.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
data class TrieNode(
2+
var isWord: Boolean = false,
3+
val children: HashMap<Char, TrieNode> = hashMapOf()
4+
)
5+
6+
class Trie {
7+
val root = TrieNode()
8+
9+
fun insert(word: String) {
10+
var node = root
11+
for (c in word.toCharArray()) {
12+
// For each character in the word, if it's not a child of
13+
// the current node, create a new TrieNode for that
14+
// character.
15+
if (c !in node.children) {
16+
node.children[c] = TrieNode()
17+
}
18+
node = node.children[c]!!
19+
}
20+
// Mark the last node as the end of a word.
21+
node.isWord = true
22+
}
23+
24+
fun search(word: String): Boolean {
25+
var node = root
26+
for (c in word.toCharArray()) {
27+
// For each character in the word, if it's not a child of
28+
// the current node, the word doesn't exist in the Trie.
29+
if (c !in node.children) {
30+
return false
31+
}
32+
node = node.children[c]!!
33+
}
34+
// Return whether the current node is marked as the end of the word.
35+
return node.isWord
36+
}
37+
38+
fun hasPrefix(prefix: String): Boolean {
39+
var node = root
40+
for (c in prefix.toCharArray()) {
41+
if (c !in node.children) {
42+
return false
43+
}
44+
node = node.children[c]!!
45+
}
46+
// Once we've traversed the nodes corresponding to each
47+
// character in the prefix, return True.
48+
return true
49+
}
50+
}

kotlin/Tries/FindAllWordsOnABoard.kt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
data class TrieNode(
2+
var word: String? = null,
3+
val children: HashMap<Char, TrieNode> = hashMapOf()
4+
)
5+
6+
fun findAllWordsOnABoard(board: Array<CharArray>, words: List<String>): List<String> {
7+
val root = TrieNode()
8+
// Insert every word into the trie.
9+
for (word in words) {
10+
var node = root
11+
for (c in word.toCharArray()) {
12+
if (c !in node.children) {
13+
node.children[c] = TrieNode()
14+
}
15+
node = node.children[c]!!
16+
}
17+
node.word = word
18+
}
19+
val res = mutableListOf<String>()
20+
// Start a DFS call from each cell of the board that contains a
21+
// child of the root node, which represents the first letter of a
22+
// word in the trie.
23+
for (r in 0 until board.size) {
24+
for (c in 0 until board[0].size) {
25+
if (board[r][c] in root.children) {
26+
dfs(board, r, c, root.children[board[r][c]], res)
27+
}
28+
}
29+
}
30+
return res
31+
}
32+
33+
fun dfs(board: Array<CharArray>, r: Int, c: Int, node: TrieNode?, res: MutableList<String>) {
34+
// If the current node represents the end of a word, add the word to
35+
// the result.
36+
if (node?.word != null) {
37+
res.add(node.word!!)
38+
// Ensure the current word is only added once.
39+
node.word = null
40+
}
41+
val temp = board[r][c]
42+
// Mark the current cell as visited.
43+
board[r][c] = '#'
44+
// Explore all adjacent cells that correspond with a child of the
45+
// current TrieNode.
46+
val dirs = listOf(-1 to 0, 1 to 0, 0 to -1, 0 to 1)
47+
for ((dr, dc) in dirs) {
48+
val nextR = r + dr
49+
val nextC = c + dc
50+
if (isWithinBounds(board, nextR, nextC) && board[nextR][nextC] in node?.children.orEmpty()) {
51+
dfs(board, nextR, nextC, node?.children?.get(board[nextR][nextC]), res)
52+
}
53+
// Backtrack by reverting the cell back to its original character.
54+
board[r][c] = temp
55+
}
56+
}
57+
58+
fun isWithinBounds(board: Array<CharArray>, r: Int, c: Int): Boolean {
59+
return r in board.indices && c in board[0].indices
60+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
data class TrieNode(
2+
var isWord: Boolean = false,
3+
val children: HashMap<Char, TrieNode> = hashMapOf()
4+
)
5+
6+
class InsertAndSearchWordsWithWildcards {
7+
private val root = TrieNode()
8+
9+
fun insert(word: String) {
10+
var node = root
11+
for (c in word) {
12+
if (c !in node.children) {
13+
node.children[c] = TrieNode()
14+
}
15+
node = node.children[c]!!
16+
}
17+
node.isWord = true
18+
}
19+
20+
fun search(word: String): Boolean {
21+
// Start searching from the root of the trie.
22+
return searchHelper(0, word, root)
23+
}
24+
25+
fun searchHelper(wordIndex: Int, word: String, node: TrieNode): Boolean {
26+
var node = node
27+
for (i in wordIndex until word.length) {
28+
val c = word[i]
29+
if (c == '.') {
30+
for (child in node.children.values) {
31+
// If a match is found, return true.
32+
if (searchHelper(i + 1, word, child)) {
33+
return true
34+
}
35+
}
36+
} else if (c in node.children) {
37+
node = node.children[c]!!
38+
} else {
39+
return false
40+
}
41+
// After processing the last character, return true if we've
42+
// reached the end of a word.
43+
}
44+
return node.isWord
45+
}
46+
}

0 commit comments

Comments
 (0)