|
| 1 | +# https://leetcode.com/problems/word-search/ |
| 2 | + |
1 | 3 | # board: 영문 대/소문자 배열, m * n |
2 | 4 | # board의 상하좌우로 인접한 문자열을 이어붙여서 word를 완성할 수 있는지 여부를 반환한다. |
3 | 5 | # 단, 셀은 한번만 사용 가능하다 |
4 | 6 |
|
5 | | -# dfs를 통해 백트랙킹으로 정답을 찾도록 구현했다. |
| 7 | +# [접근법] |
| 8 | +# dfs로 문자열을 상,하,좌,우로 탐색한다. |
6 | 9 |
|
| 10 | +# [복잡도] |
7 | 11 | # L: word 의 길이 |
8 | | -# 시간 복잡도: O(m * n * 4^L) |
9 | | -# 공간 복잡도: O(m * n + L) |
10 | | -class SolutionA: |
| 12 | +# 시간 복잡도: O(m * n * 3^L), 이전 문자에서 이동한 방향은 제외하므로 3^L |
| 13 | +# 공간 복잡도: O(L) |
| 14 | + |
| 15 | +class Solution: |
11 | 16 | def exist(self, board: List[List[str]], word: str) -> bool: |
12 | 17 | row_size = len(board) |
13 | 18 | col_size = len(board[0]) |
14 | 19 |
|
15 | | - # 방문 여부를 기록하는 2차원 배열 |
16 | | - visited = [[False] * col_size for _ in range(row_size)] |
17 | | - |
18 | | - def dfs(row, col, str_idx) -> bool: |
19 | | - if str_idx == len(word): |
20 | | - return True |
21 | | - |
22 | | - # 종료조건1: 보드의 좌표를 넘어가는 경우 |
23 | | - if row < 0 or row >= row_size or col < 0 or col >= col_size: |
| 20 | + # board에 word를 만들 수 있는 문자열이 충분히 있는지 확인 |
| 21 | + for char in set(word): |
| 22 | + # board 전체에서 해당 알파벳이 몇 개인지 카운트한다. |
| 23 | + board_char_count = sum(row.count(char) for row in board) |
| 24 | + if board_char_count < word.count(char): |
24 | 25 | return False |
25 | | - |
26 | | - # 종료조건 2: 이미 방문함 |
27 | | - # 종료조건 3: 문자열 불일치 |
28 | | - if visited[row][col] or board[row][col] != word[str_idx]: |
29 | | - return False |
30 | | - |
31 | | - |
32 | | - visited[row][col] = True |
33 | | - |
34 | | - # 차례대로 상, 하, 좌, 우의 좌표를 탐색 |
35 | | - found = (dfs(row - 1, col, str_idx + 1) or \ |
36 | | - dfs(row + 1, col, str_idx + 1) or \ |
37 | | - dfs(row, col - 1, str_idx + 1) or \ |
38 | | - dfs(row, col + 1, str_idx + 1)) |
39 | | - |
40 | | - visited[row][col] = False # 백트래킹 |
41 | | - |
42 | | - return found |
43 | 26 |
|
| 27 | + # 빈도수가 더 적은 글자부터 탐색하도록 단어를 뒤집는다 |
| 28 | + count_first = sum(row.count(word[0]) for row in board) |
| 29 | + count_last = sum(row.count(word[-1]) for row in board) |
| 30 | + |
| 31 | + if count_first > count_last: |
| 32 | + word = word[::-1] |
44 | 33 |
|
45 | | - # 시작점을 찾는다 |
46 | | - for r in range(row_size): |
47 | | - for c in range(col_size): |
48 | | - if board[r][c] == word[0] and dfs(r, c, 0): |
49 | | - return True |
50 | | - |
51 | | - return False |
52 | | - |
53 | | -# SolutionA 의 공간복잡도 최적화 버전, visited 제거 |
54 | | -# L: word 의 길이 |
55 | | -# 시간 복잡도: O(m * n * 4^L) |
56 | | -# 공간 복잡도: O(L) |
57 | | -class Solution: |
58 | | - def exist(self, board: List[List[str]], word: str) -> bool: |
59 | 34 | VISITED_MARK = '#' |
60 | | - |
61 | | - row_size = len(board) |
62 | | - col_size = len(board[0]) |
63 | | - |
64 | | - def dfs(row, col, str_idx) -> bool: |
| 35 | + def dfs(row: int, col: int, str_idx: int) -> bool: |
65 | 36 | if str_idx == len(word): |
66 | 37 | return True |
67 | 38 |
|
68 | 39 | # 종료조건1: 보드의 좌표를 넘어가는 경우 |
69 | | - if row < 0 or row >= row_size or col < 0 or col >= col_size: |
| 40 | + if not (0 <= row < row_size and 0 <= col < col_size): |
70 | 41 | return False |
71 | 42 |
|
72 | | - # 종료조건 2: 이미 방문함 |
| 43 | + # 종료조건 2: 이미 방문함 (#: 방문했음을 표시하는 마크) |
73 | 44 | # 종료조건 3: 문자열 불일치 |
74 | 45 | if board[row][col] == VISITED_MARK or board[row][col] != word[str_idx]: |
75 | 46 | return False |
76 | | - |
77 | 47 |
|
78 | | - # VISITED_MARK로 교체하여 방문을 기록 |
| 48 | + # 방문을 표시하는 마크(#)로 임시 교체 |
79 | 49 | origin_char = board[row][col] |
80 | 50 | board[row][col] = VISITED_MARK |
81 | 51 |
|
82 | | - # 차례대로 상, 하, 좌, 우의 좌표를 탐색 |
83 | | - found = (dfs(row - 1, col, str_idx + 1) or \ |
84 | | - dfs(row + 1, col, str_idx + 1) or \ |
85 | | - dfs(row, col - 1, str_idx + 1) or \ |
86 | | - dfs(row, col + 1, str_idx + 1)) |
| 52 | + # 좌표를 탐색한다 |
| 53 | + found = ( |
| 54 | + dfs(row - 1, col, str_idx + 1) # 상 |
| 55 | + or dfs(row + 1, col, str_idx + 1) # 하 |
| 56 | + or dfs(row, col - 1, str_idx + 1) # 좌 |
| 57 | + or dfs(row, col + 1, str_idx + 1) # 우 |
| 58 | + ) |
87 | 59 |
|
88 | 60 | # 원본 문자열로 복원 |
89 | 61 | board[row][col] = origin_char |
|
0 commit comments