Skip to content

Commit eae7549

Browse files
alexpantyukhingithub-actionspre-commit-ci[bot]cclauss
authored andcommitted
Add backtrack word search in matrix (TheAlgorithms#8005)
* add backtracking word search * updating DIRECTORY.md * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * review notes fixes * additional fixes * add tests * small cleanup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * small cleanup 2 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update backtracking/word_search.py Co-authored-by: Christian Clauss <[email protected]> * Update backtracking/word_search.py Co-authored-by: Christian Clauss <[email protected]> * Update backtracking/word_search.py Co-authored-by: Christian Clauss <[email protected]> * Update backtracking/word_search.py Co-authored-by: Christian Clauss <[email protected]> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <[email protected]>
1 parent 15c9973 commit eae7549

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* [Rat In Maze](backtracking/rat_in_maze.py)
3434
* [Sudoku](backtracking/sudoku.py)
3535
* [Sum Of Subsets](backtracking/sum_of_subsets.py)
36+
* [Word Search](backtracking/word_search.py)
3637

3738
## Bit Manipulation
3839
* [Binary And Operator](bit_manipulation/binary_and_operator.py)

backtracking/word_search.py

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Author : Alexander Pantyukhin
3+
Date : November 24, 2022
4+
5+
Task:
6+
Given an m x n grid of characters board and a string word,
7+
return true if word exists in the grid.
8+
9+
The word can be constructed from letters of sequentially adjacent cells,
10+
where adjacent cells are horizontally or vertically neighboring.
11+
The same letter cell may not be used more than once.
12+
13+
Example:
14+
15+
Matrix:
16+
---------
17+
|A|B|C|E|
18+
|S|F|C|S|
19+
|A|D|E|E|
20+
---------
21+
22+
Word:
23+
"ABCCED"
24+
25+
Result:
26+
True
27+
28+
Implementation notes: Use backtracking approach.
29+
At each point, check all neighbors to try to find the next letter of the word.
30+
31+
leetcode: https://leetcode.com/problems/word-search/
32+
33+
"""
34+
35+
36+
def word_exists(board: list[list[str]], word: str) -> bool:
37+
"""
38+
>>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "ABCCED")
39+
True
40+
>>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "SEE")
41+
True
42+
>>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "ABCB")
43+
False
44+
>>> word_exists([["A"]], "A")
45+
True
46+
>>> word_exists([["A","A","A","A","A","A"],
47+
... ["A","A","A","A","A","A"],
48+
... ["A","A","A","A","A","A"],
49+
... ["A","A","A","A","A","A"],
50+
... ["A","A","A","A","A","B"],
51+
... ["A","A","A","A","B","A"]],
52+
... "AAAAAAAAAAAAABB")
53+
False
54+
>>> word_exists([["A"]], 123)
55+
Traceback (most recent call last):
56+
...
57+
ValueError: The word parameter should be a string of length greater than 0.
58+
>>> word_exists([["A"]], "")
59+
Traceback (most recent call last):
60+
...
61+
ValueError: The word parameter should be a string of length greater than 0.
62+
>>> word_exists([[]], "AB")
63+
Traceback (most recent call last):
64+
...
65+
ValueError: The board should be a non empty matrix of single chars strings.
66+
>>> word_exists([], "AB")
67+
Traceback (most recent call last):
68+
...
69+
ValueError: The board should be a non empty matrix of single chars strings.
70+
>>> word_exists([["A"], [21]], "AB")
71+
Traceback (most recent call last):
72+
...
73+
ValueError: The board should be a non empty matrix of single chars strings.
74+
"""
75+
76+
# Validate board
77+
board_error_message = (
78+
"The board should be a non empty matrix of single chars strings."
79+
)
80+
if not isinstance(board, list) or len(board) == 0:
81+
raise ValueError(board_error_message)
82+
83+
for row in board:
84+
if not isinstance(row, list) or len(row) == 0:
85+
raise ValueError(board_error_message)
86+
87+
for item in row:
88+
if not isinstance(item, str) or len(item) != 1:
89+
raise ValueError(board_error_message)
90+
91+
# Validate word
92+
if not isinstance(word, str) or len(word) == 0:
93+
raise ValueError(
94+
"The word parameter should be a string of length greater than 0."
95+
)
96+
97+
traverts_directions = [(0, 1), (0, -1), (-1, 0), (1, 0)]
98+
len_word = len(word)
99+
len_board = len(board)
100+
len_board_column = len(board[0])
101+
102+
# Returns the hash key of matrix indexes.
103+
def get_point_key(row: int, column: int) -> int:
104+
"""
105+
>>> len_board=10
106+
>>> len_board_column=20
107+
>>> get_point_key(0, 0)
108+
200
109+
"""
110+
111+
return len_board * len_board_column * row + column
112+
113+
# Return True if it's possible to search the word suffix
114+
# starting from the word_index.
115+
def exits_word(
116+
row: int, column: int, word_index: int, visited_points_set: set[int]
117+
) -> bool:
118+
"""
119+
>>> board=[["A"]]
120+
>>> word="B"
121+
>>> exits_word(0, 0, 0, set())
122+
False
123+
"""
124+
125+
if board[row][column] != word[word_index]:
126+
return False
127+
128+
if word_index == len_word - 1:
129+
return True
130+
131+
for direction in traverts_directions:
132+
next_i = row + direction[0]
133+
next_j = column + direction[1]
134+
if not (0 <= next_i < len_board and 0 <= next_j < len_board_column):
135+
continue
136+
137+
key = get_point_key(next_i, next_j)
138+
if key in visited_points_set:
139+
continue
140+
141+
visited_points_set.add(key)
142+
if exits_word(next_i, next_j, word_index + 1, visited_points_set):
143+
return True
144+
145+
visited_points_set.remove(key)
146+
147+
return False
148+
149+
for i in range(len_board):
150+
for j in range(len_board_column):
151+
if exits_word(i, j, 0, {get_point_key(i, j)}):
152+
return True
153+
154+
return False
155+
156+
157+
if __name__ == "__main__":
158+
import doctest
159+
160+
doctest.testmod()

0 commit comments

Comments
 (0)