diff --git a/config.json b/config.json index cf854d48..ed915bae 100644 --- a/config.json +++ b/config.json @@ -1194,6 +1194,17 @@ "loops" ] }, + { + "slug": "word-search", + "name": "Word Search", + "uuid": "e88da49b-37d7-4f84-ad64-765fb464a721", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "topics": [ + "algorithms" + ] + }, { "slug": "bowling", "name": "Bowling", diff --git a/exercises/practice/word-search/.docs/instructions.md b/exercises/practice/word-search/.docs/instructions.md new file mode 100644 index 00000000..e2d08aa9 --- /dev/null +++ b/exercises/practice/word-search/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +In word search puzzles you get a square of letters and have to find specific words in them. + +For example: + +```text +jefblpepre +camdcimgtc +oivokprjsm +pbwasqroua +rixilelhrs +wolcqlirpc +screeaumgr +alxhpburyi +jalaycalmp +clojurermt +``` + +There are several programming languages hidden in the above square. + +Words can be hidden in all kinds of directions: left-to-right, right-to-left, vertical and diagonal. + +Given a puzzle and a list of words return the location of the first and last letter of each word. diff --git a/exercises/practice/word-search/.meta/Sources/WordSearch/TrieNode.swift b/exercises/practice/word-search/.meta/Sources/WordSearch/TrieNode.swift new file mode 100644 index 00000000..fc2deb0d --- /dev/null +++ b/exercises/practice/word-search/.meta/Sources/WordSearch/TrieNode.swift @@ -0,0 +1,57 @@ + +class TrieNode { + var children: [Character: TrieNode] = [:] + var word: String? = nil +} + +extension TrieNode { + + static func buildTrie(_ words: [String]) -> TrieNode { + let root = TrieNode() + for word in words { + var node = root + for char in word { + if node.children[char] == nil { + node.children[char] = TrieNode() + } + node = node.children[char]! + } + node.word = word + } + return root + } + + static func dfs( + board: [[Character]], + initialRow: Int, + initialColumn: Int, + row: Int, + column: Int, + direction: (Int, Int), + node: TrieNode, + result: inout [String: WordLocation?] + ) { + guard row >= 0, row < board.count, column >= 0, column < board[0].count else { return } + + let char = board[row][column] + guard let nextNode = node.children[char] else { return } + + if let word = nextNode.word { + let start = WordLocation.Location(row: initialRow + 1, column: initialColumn + 1) + let end = WordLocation.Location(row: row + 1, column: column + 1) + result[word] = WordLocation(start: start, end: end) + } + + dfs( + board: board, + initialRow: initialRow, + initialColumn: initialColumn, + row: row + direction.0, + column: column + direction.1, + direction: direction, + node: nextNode, + result: &result + ) + } + +} diff --git a/exercises/practice/word-search/.meta/Sources/WordSearch/WordLocation.swift b/exercises/practice/word-search/.meta/Sources/WordSearch/WordLocation.swift new file mode 100644 index 00000000..b7913365 --- /dev/null +++ b/exercises/practice/word-search/.meta/Sources/WordSearch/WordLocation.swift @@ -0,0 +1,12 @@ + +struct WordLocation: Equatable { + + struct Location: Equatable { + let row: Int + let column: Int + } + + let start: Location + let end: Location + +} \ No newline at end of file diff --git a/exercises/practice/word-search/.meta/Sources/WordSearch/WordSearchExample.swift b/exercises/practice/word-search/.meta/Sources/WordSearch/WordSearchExample.swift new file mode 100644 index 00000000..f7280346 --- /dev/null +++ b/exercises/practice/word-search/.meta/Sources/WordSearch/WordSearchExample.swift @@ -0,0 +1,30 @@ +import Foundation + +func search(words: [String], in grid: [String]) -> [String: WordLocation?] { + let board: [[Character]] = grid.map { Array($0) } + let trie = TrieNode.buildTrie(words) + var result = [String: WordLocation?]() + + let directions = [ + (-1,0), (1,0), (0,-1), (0,1), + (-1,-1), (1,1), (1,-1), (-1,1) + ] + for row in 0..