Skip to content

Commit 369dc9a

Browse files
committed
add solution: word search ii
1 parent 6e9197f commit 369dc9a

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

word-search-ii/flynn.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
풀이
3+
- Trie 자료구조와 2차원 배열에서의 DFS & Backtracking을 사용하여 풀이할 수 있습니다
4+
Big O
5+
- M, N: 주어진 2차원 배열 board의 행, 열 크기
6+
- Max(W): 주어진 배열 words 중에서 가장 길이가 긴 단어의 길이
7+
- Sum(W): 주어진 배열 words에 포함된 모든 단어의 길이의 총합
8+
- Time complexity: O(Sum(W) + MN * Max(W))
9+
- building trie: O(Sum(W))
10+
- 각 단어의 모든 문자를 한 번씩 조회합니다
11+
- 반복문: O(MN * Max(W))
12+
- 행, 열에 대한 반복: O(MN)
13+
- dfs: O(Max(W))
14+
- 한 좌표에 대해 dfs 함수를 trie의 최대 깊이, 즉 O(Max(W))만큼 호출합니다
15+
- Space complexity: O(Sum(W) + Max(W))
16+
- building trie: O(Sum(W)) at worst
17+
- dfs: O(Max(W))
18+
- 재귀 호출 스택의 깊이를 고려해야 합니다
19+
*/
20+
21+
func findWords(board [][]byte, words []string) []string {
22+
// building trie
23+
root := &trieNode{}
24+
for _, w := range words {
25+
root.add(w)
26+
}
27+
28+
m := len(board)
29+
n := len(board[0])
30+
res := make([]string, 0, len(words)) // create result
31+
for r := 0; r < m; r++ {
32+
for c := 0; c < n; c++ {
33+
if len(res) == len(words) { // early break if we found all the words
34+
break
35+
}
36+
if root.children[idx(board[r][c])] != nil { // dfs if you found starting letter of any words
37+
dfs(r, c, &board, root, &res)
38+
}
39+
}
40+
}
41+
42+
return res
43+
}
44+
45+
// ----- Implementation of TrieNode -----
46+
type trieNode struct {
47+
children [26]*trieNode
48+
word string
49+
}
50+
51+
func (t *trieNode) add(w string) {
52+
for i, c := range w {
53+
if t.children[c-'a'] == nil { // create new child node if there wasn't
54+
t.children[c-'a'] = &trieNode{}
55+
}
56+
t = t.children[c-'a']
57+
if i == len(w)-1 {
58+
t.word = w
59+
}
60+
}
61+
}
62+
63+
// ----- Dfs -----
64+
func dfs(r, c int, board *[][]byte, parentNode *trieNode, res *[]string) {
65+
currLetter := (*board)[r][c]
66+
currNode := parentNode.children[idx(currLetter)]
67+
// if current trie node represents some word, then append it to the result
68+
if currNode.word != "" {
69+
*res = append(*res, currNode.word)
70+
currNode.word = "" // prevent duplicate words into the result
71+
}
72+
// mark current cell as visited
73+
(*board)[r][c] = '#'
74+
// search for the 4 directions
75+
m := len(*board)
76+
n := len((*board)[0])
77+
dr := []int{0, 0, 1, -1}
78+
dc := []int{1, -1, 0, 0}
79+
for i := 0; i < 4; i++ {
80+
nr := r + dr[i]
81+
nc := c + dc[i]
82+
if !(0 <= nr && nr < m) || !(0 <= nc && nc < n) { // skip the invalid coordinates
83+
continue
84+
}
85+
if (*board)[nr][nc] == '#' { // skip the visited cell
86+
continue
87+
}
88+
if currNode.children[idx((*board)[nr][nc])] != nil {
89+
dfs(nr, nc, board, currNode, res)
90+
}
91+
}
92+
// restore the cell
93+
(*board)[r][c] = currLetter
94+
}
95+
96+
// ----- Helper -----
97+
func idx(b byte) int {
98+
return int(b - 'a')
99+
}

0 commit comments

Comments
 (0)