← 전체 회차
a-063-word-search-ii2026-07-15hardleetcode #212neetcode150

단어 검색 II

#array#string#backtracking#trie#matrix
leetcode #212 · a-063-word-search-ii
01

문제

· problem
P.a-063-word-search-ii

단어 검색 II

leetcode #212

m × n 크기의 문자 보드와 문자열 목록이 주어졌을 때, 보드에 있는 모든 단어를 반환하세요. 각 단어는 인접한 셀(가로 또는 세로로 이웃한)의 문자로 순서대로 구성되어야 합니다. 같은 문자 셀을 한 단어에 두 번 이상 사용할 수 없습니다.

제약
  • · 1 ≤ m, n ≤ 12
  • · board[i][j] is a lowercase English letter
  • · 1 ≤ words.length ≤ 3 × 10⁴
  • · 1 ≤ words[i].length ≤ 10
// 지문은 본인 언어 요약 — 원문은 위 링크에서
입출력 예시
example 1input → output
[["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]
["oath","pea","eat","rain"]
["eat","oath"]
example 2input → output
[["a","b"],["c","d"]]
["abcb"]
[]
02

사전 사고

· pre-solve
● 1리스트 출력● 2선택● 3정답 공개
  • 한 단어를 검색할 때 같은 셀을 두 번 이상 사용할 수 있나요?
  • 인접(adjacent)이란 상하좌우만 해당하나요, 대각선도 포함하나요?
  • 보드에서 한 단어가 여러 번 나타나면 여러 번 반환해야 하나요?
  • Trie의 'refs' 카운터는 왜 필요한가요?
  • 단어를 찾은 후 'removeWord'를 호출하는 이유는?
  • 왜 대신 'isWord = False'만 설정하고 removeWord는 호출하지 않으면 안 되나요?
  • DFS에서 4가지 방향 호출 순서가 중요한가요?
던질 질문에 체크하고 확인을 누르세요
// 결과는 세션 메모리만 — 새로고침하면 초기화됩니다 (반복 학습)
03

논리 구조

· logic
● 1슬롯 출력● 2슬롯별 선택● 3정답 공개
// 각 슬롯에 들어갈 코드 한 줄을 골라 알고리즘 흐름을 합성해보세요. 코드는 안 짜지만 논리 뼈대는 직접.
step 1· Trie에 모든 단어 추가
root.addWord(w)
words_set = set(words); word_dict = {w: True for w in words}
for w in sorted(words): root.addWord(w)
for w in words: root.addWord(w[::-1])
step 2· 검색 상태 초기화
res, visit = set(), set()
res, visit = [], set()
res, visit = set(), []
res, visit, found_words = set(), set(), set()
step 3· 경계 및 가지 제거 검사중첩
or node.children[board[r][c]].refs < 1
or node.children[board[r][c]].refs == 0
or board[r][c] not in node.children
or len(node.children) == 0
step 4· 발견한 단어 제거 및 처리중첩
root.removeWord(word)
node.isWord = False  # without removeWord
res.add(word); visit.clear()
res.discard(word)  # remove from result?
step 5· 모든 인접 셀 탐색중첩
dfs(r + 1, c, node, word)
dfs(r + 1, c, node, word); dfs(r - 1, c, node, word)  # only vertical
dfs(r + 1, c + 1, node, word)  # diagonal
# only dfs(r + 1, c, node, word)
step 6· 상태 복원 (백트래킹)중첩
visit.remove((r, c))
# no visit.remove — missing backtrack
visit = set()  # clear entire visited set
if (r, c) in visit: visit.remove((r, c))
각 슬롯에 한 줄씩 골라보세요
// format: slot — 다른 패턴(재귀·DP 등) 은 ordering·state-first 등 별도 format. ADR-08 후속.
04

문제풀이 · 트레이스

· solve
solution.py
1
class TrieNode:
2
    def __init__(self):
3
        self.children = {}
4
        self.isWord = False
5
        self.refs = 0
6
7
    def addWord(self, word):
8
        cur = self
9
        cur.refs += 1
10
        for c in word:
11
            if c not in cur.children:
12
                cur.children[c] = TrieNode()
13
            cur = cur.children[c]
14
            cur.refs += 1
15
        cur.isWord = True
16
17
    def removeWord(self, word):
18
        cur = self
19
        cur.refs -= 1
20
        for c in word:
21
            if c in cur.children:
22
                cur = cur.children[c]
23
                cur.refs -= 1
24
25
26
class Solution:
27
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
28
        root = TrieNode()
29
        for w in words:
30
            root.addWord(w)
31
32
        ROWS, COLS = len(board), len(board[0])
33
        res, visit = set(), set()
34
35
        def dfs(r, c, node, word):
36
            if (
37
                r not in range(ROWS) 
38
                or c not in range(COLS)
39
                or board[r][c] not in node.children
40
                or node.children[board[r][c]].refs < 1
41
                or (r, c) in visit
42
            ):
43
                return
44
45
            visit.add((r, c))
46
            node = node.children[board[r][c]]
47
            word += board[r][c]
48
            if node.isWord:
49
                node.isWord = False
50
                res.add(word)
51
                root.removeWord(word)
52
53
            dfs(r + 1, c, node, word)
54
            dfs(r - 1, c, node, word)
55
            dfs(r, c + 1, node, word)
56
            dfs(r, c - 1, node, word)
57
            visit.remove((r, c))
58
59
        for r in range(ROWS):
60
            for c in range(COLS):
61
                dfs(r, c, root, "")
62
63
        return list(res)
머릿속 dry-run 케이스
// 각 케이스를 머릿속으로 따라가보세요. 막히면 아래 worked example 펼침.
case 1
[["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]]
["oath","pea","eat","rain"]
["eat","oath"]
case 2
[["a","b"],["c","d"]]
["abcb"]
[]
// UI 가 walk-through 안 함 — 학습자가 머릿속으로. 막히면 worked example 펼침.