From 7972862f5009dda336f1438b6edd3e7f33f3bbe1 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Mon, 28 Nov 2022 11:10:31 +0400 Subject: [PATCH 01/14] add backtracking word search --- backtracking/word_search.py | 164 ++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 backtracking/word_search.py diff --git a/backtracking/word_search.py b/backtracking/word_search.py new file mode 100644 index 000000000000..b3d2eae1cf3e --- /dev/null +++ b/backtracking/word_search.py @@ -0,0 +1,164 @@ +""" +Author : Alexander Pantyukhin +Date : November 24, 2022 + +Task: +Given an m x n grid of characters board and a string word, +return true if word exists in the grid. + +The word can be constructed from letters of sequentially adjacent cells, +where adjacent cells are horizontally or vertically neighboring. +The same letter cell may not be used more than once. + +Example: + +Matrix: +--------- +|A|B|C|E| +|S|F|C|S| +|A|D|E|E| +--------- + +Word: +"ABCCED" + +Result: +True + +Implementation notes: Use backtracking approach. +For each point need to check neigh neighbours +and try to find left suffix of the word. + +leetcode: https://leetcode.com/problems/word-search/ + +""" + + +def word_exists(board: list[list[str]], word: str) -> bool: + """ + >>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "ABCCED") + True + >>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "SEE") + True + >>> word_exists([["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], "ABCB") + False + >>> word_exists([["A"]], "A") + True + >>> word_exists([["A","A","A","A","A","A"],\ + ["A","A","A","A","A","A"],\ + ["A","A","A","A","A","A"],\ + ["A","A","A","A","A","A"],\ + ["A","A","A","A","A","B"],\ + ["A","A","A","A","B","A"]],\ + "AAAAAAAAAAAAABB") + False + >>> word_exists([["A"]], 123) + Traceback (most recent call last): + ... + ValueError: The word parameter should be a string of length greater than 0. + >>> word_exists([["A"]], "") + Traceback (most recent call last): + ... + ValueError: The word parameter should be a string of length greater than 0. + >>> word_exists([[]], "AB") + Traceback (most recent call last): + ... + ValueError: The board should be a non empty matrix of single chars strings. + >>> word_exists([], "AB") + Traceback (most recent call last): + ... + ValueError: The board should be a non empty matrix of single chars strings. + >>> word_exists([["A"], [21]], "AB") + Traceback (most recent call last): + ... + ValueError: The board should be a non empty matrix of single chars strings. + """ + + def validate_board(board: list[list[str]]): + """ + >>> validate_board([[]]) + Traceback (most recent call last): + ... + ValueError: The board should be a non empty matrix of single chars strings. + >>> validate_board([]) + Traceback (most recent call last): + ... + ValueError: The board should be a non empty matrix of single chars strings. + """ + + # Validate board + error_message = "The board should be a non empty matrix of single chars strings." + if not isinstance(board, list) or len(board) == 0: + raise ValueError(error_message) + + for row in board: + if not isinstance(row, list) or len(row) == 0: + raise ValueError(error_message) + + for item in row: + if not isinstance(item, str) or len(item) != 1: + raise ValueError(error_message) + + # Validation + validate_board(board) + if not isinstance(word, str) or len(word) == 0: + raise ValueError("The word parameter should be a string of length greater than 0.") + + traverts_directions = [(0, 1), (0, -1), (-1, 0), (1, 0)] + len_word = len(word) + len_board = len(board) + len_board_column = len(board[0]) + + # Returns the hash key of matrix indexes. + def get_point_key(i: int, j: int) -> int: + return len_board * len_board_column * i + j + + # Return True if it's possible to search the word suffix + # starting from the word_index. + def exits_word(i: int, + j: int, + word_index:int, + visited_points_set: set[int]) -> bool: + + if board[i][j] != word[word_index]: + return False + + if word_index == len_word - 1: + return True + + for direction in traverts_directions: + next_i = i + direction[0] + next_j = j + direction[1] + if next_i < 0 or \ + next_i >= len_board or \ + next_j < 0 or \ + next_j >= len_board_column: + continue + + key = get_point_key(next_i, next_j) + if key in visited_points_set: + continue + + visited_points_set.add(key) + if exits_word(next_i, + next_j, + word_index + 1, + visited_points_set): + return True + + visited_points_set.remove(key) + + return False + + for i in range(len_board): + for j in range(len_board_column): + if exits_word(i, j, 0, {get_point_key(i, j)}): + return True + + return False + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From b05215ab2f28bfdb314a96bb83c8e0db073cd9b7 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 28 Nov 2022 07:11:50 +0000 Subject: [PATCH 02/14] updating DIRECTORY.md --- DIRECTORY.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 83da4b76abca..51430a1e159e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -33,6 +33,7 @@ * [Rat In Maze](backtracking/rat_in_maze.py) * [Sudoku](backtracking/sudoku.py) * [Sum Of Subsets](backtracking/sum_of_subsets.py) + * [Word Search](backtracking/word_search.py) ## Bit Manipulation * [Binary And Operator](bit_manipulation/binary_and_operator.py) @@ -375,6 +376,7 @@ * [Articulation Points](graphs/articulation_points.py) * [Basic Graphs](graphs/basic_graphs.py) * [Bellman Ford](graphs/bellman_ford.py) + * [Bi Directional Dijkstra](graphs/bi_directional_dijkstra.py) * [Bidirectional A Star](graphs/bidirectional_a_star.py) * [Bidirectional Breadth First Search](graphs/bidirectional_breadth_first_search.py) * [Boruvka](graphs/boruvka.py) @@ -563,6 +565,7 @@ * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Square Free](maths/is_square_free.py) * [Jaccard Similarity](maths/jaccard_similarity.py) + * [Juggler Sequence](maths/juggler_sequence.py) * [Kadanes](maths/kadanes.py) * [Karatsuba](maths/karatsuba.py) * [Krishnamurthy Number](maths/krishnamurthy_number.py) From 259300fbcf8d8127e9b2379b4119b7da736aeae3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 07:13:28 +0000 Subject: [PATCH 03/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/word_search.py | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index b3d2eae1cf3e..3f3951868204 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -87,7 +87,9 @@ def validate_board(board: list[list[str]]): """ # Validate board - error_message = "The board should be a non empty matrix of single chars strings." + error_message = ( + "The board should be a non empty matrix of single chars strings." + ) if not isinstance(board, list) or len(board) == 0: raise ValueError(error_message) @@ -102,7 +104,9 @@ def validate_board(board: list[list[str]]): # Validation validate_board(board) if not isinstance(word, str) or len(word) == 0: - raise ValueError("The word parameter should be a string of length greater than 0.") + raise ValueError( + "The word parameter should be a string of length greater than 0." + ) traverts_directions = [(0, 1), (0, -1), (-1, 0), (1, 0)] len_word = len(word) @@ -115,39 +119,37 @@ def get_point_key(i: int, j: int) -> int: # Return True if it's possible to search the word suffix # starting from the word_index. - def exits_word(i: int, - j: int, - word_index:int, - visited_points_set: set[int]) -> bool: + def exits_word( + i: int, j: int, word_index: int, visited_points_set: set[int] + ) -> bool: if board[i][j] != word[word_index]: return False if word_index == len_word - 1: return True - + for direction in traverts_directions: next_i = i + direction[0] next_j = j + direction[1] - if next_i < 0 or \ - next_i >= len_board or \ - next_j < 0 or \ - next_j >= len_board_column: + if ( + next_i < 0 + or next_i >= len_board + or next_j < 0 + or next_j >= len_board_column + ): continue key = get_point_key(next_i, next_j) if key in visited_points_set: continue - + visited_points_set.add(key) - if exits_word(next_i, - next_j, - word_index + 1, - visited_points_set): + if exits_word(next_i, next_j, word_index + 1, visited_points_set): return True - + visited_points_set.remove(key) - + return False for i in range(len_board): From 743065340064993e251649de7c9babaa3b78d1ca Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Mon, 28 Nov 2022 11:17:02 +0400 Subject: [PATCH 04/14] review notes fixes --- backtracking/word_search.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 3f3951868204..23764a74e9c0 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -74,7 +74,7 @@ def word_exists(board: list[list[str]], word: str) -> bool: ValueError: The board should be a non empty matrix of single chars strings. """ - def validate_board(board: list[list[str]]): + def validate_board(board: list[list[str]]) -> None: """ >>> validate_board([[]]) Traceback (most recent call last): @@ -120,18 +120,18 @@ def get_point_key(i: int, j: int) -> int: # Return True if it's possible to search the word suffix # starting from the word_index. def exits_word( - i: int, j: int, word_index: int, visited_points_set: set[int] + row: int, column: int, word_index: int, visited_points_set: set[int] ) -> bool: - if board[i][j] != word[word_index]: + if board[row][column] != word[word_index]: return False if word_index == len_word - 1: return True for direction in traverts_directions: - next_i = i + direction[0] - next_j = j + direction[1] + next_i = row + direction[0] + next_j = column + direction[1] if ( next_i < 0 or next_i >= len_board From 77b0e163341f359dc94acb0668190e0952a2a4bc Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Mon, 28 Nov 2022 11:18:09 +0400 Subject: [PATCH 05/14] additional fixes --- backtracking/word_search.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 23764a74e9c0..0d6d70dc4e9d 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -114,8 +114,8 @@ def validate_board(board: list[list[str]]) -> None: len_board_column = len(board[0]) # Returns the hash key of matrix indexes. - def get_point_key(i: int, j: int) -> int: - return len_board * len_board_column * i + j + def get_point_key(row: int, column: int) -> int: + return len_board * len_board_column * row + column # Return True if it's possible to search the word suffix # starting from the word_index. From 1ba52fbab1e373b0470d44bed4f67cff3472cef4 Mon Sep 17 00:00:00 2001 From: Alexander Panrtyukhin Date: Mon, 28 Nov 2022 11:56:57 +0400 Subject: [PATCH 06/14] add tests --- backtracking/word_search.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 0d6d70dc4e9d..de892193d72b 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -115,6 +115,13 @@ def validate_board(board: list[list[str]]) -> None: # Returns the hash key of matrix indexes. def get_point_key(row: int, column: int) -> int: + """ + >>> len_board=10 + >>> len_board_column=20 + >>> get_point_key(0, 0) + 200 + """ + return len_board * len_board_column * row + column # Return True if it's possible to search the word suffix @@ -122,6 +129,12 @@ def get_point_key(row: int, column: int) -> int: def exits_word( row: int, column: int, word_index: int, visited_points_set: set[int] ) -> bool: + """ + >>> board=[["A"]] + >>> word="B" + >>> exits_word(0, 0, 0, set()) + False + """ if board[row][column] != word[word_index]: return False From cb2c632f242d30b22d70ad83a43a13218d1c650e Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 11:17:51 +0400 Subject: [PATCH 07/14] small cleanup --- backtracking/word_search.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index de892193d72b..30884e2e0cc6 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -74,35 +74,22 @@ def word_exists(board: list[list[str]], word: str) -> bool: ValueError: The board should be a non empty matrix of single chars strings. """ - def validate_board(board: list[list[str]]) -> None: - """ - >>> validate_board([[]]) - Traceback (most recent call last): - ... - ValueError: The board should be a non empty matrix of single chars strings. - >>> validate_board([]) - Traceback (most recent call last): - ... - ValueError: The board should be a non empty matrix of single chars strings. - """ - - # Validate board - error_message = ( - "The board should be a non empty matrix of single chars strings." - ) - if not isinstance(board, list) or len(board) == 0: + # Validate board + error_message = ( + "The board should be a non empty matrix of single chars strings." + ) + if not isinstance(board, list) or len(board) == 0: + raise ValueError(error_message) + + for row in board: + if not isinstance(row, list) or len(row) == 0: raise ValueError(error_message) - for row in board: - if not isinstance(row, list) or len(row) == 0: + for item in row: + if not isinstance(item, str) or len(item) != 1: raise ValueError(error_message) - for item in row: - if not isinstance(item, str) or len(item) != 1: - raise ValueError(error_message) - - # Validation - validate_board(board) + # Validate word if not isinstance(word, str) or len(word) == 0: raise ValueError( "The word parameter should be a string of length greater than 0." From 86975338ba3460e42cda6d60c2f7f57ae5d93922 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 07:19:47 +0000 Subject: [PATCH 08/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/word_search.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 30884e2e0cc6..61cc4fb9c1d1 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -75,9 +75,7 @@ def word_exists(board: list[list[str]], word: str) -> bool: """ # Validate board - error_message = ( - "The board should be a non empty matrix of single chars strings." - ) + error_message = "The board should be a non empty matrix of single chars strings." if not isinstance(board, list) or len(board) == 0: raise ValueError(error_message) From 7e980745354f8428096f00a83d333301180fb527 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 11:21:13 +0400 Subject: [PATCH 09/14] small cleanup 2 --- backtracking/word_search.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 61cc4fb9c1d1..08767980aedf 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -75,17 +75,17 @@ def word_exists(board: list[list[str]], word: str) -> bool: """ # Validate board - error_message = "The board should be a non empty matrix of single chars strings." + board_error_message = "The board should be a non empty matrix of single chars strings." if not isinstance(board, list) or len(board) == 0: - raise ValueError(error_message) + raise ValueError(board_error_message) for row in board: if not isinstance(row, list) or len(row) == 0: - raise ValueError(error_message) + raise ValueError(board_error_message) for item in row: if not isinstance(item, str) or len(item) != 1: - raise ValueError(error_message) + raise ValueError(board_error_message) # Validate word if not isinstance(word, str) or len(word) == 0: From d50711a7ff97520440d4f762c357eb794638a29f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 07:23:19 +0000 Subject: [PATCH 10/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/word_search.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 08767980aedf..fe63e934084a 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -75,7 +75,9 @@ def word_exists(board: list[list[str]], word: str) -> bool: """ # Validate board - board_error_message = "The board should be a non empty matrix of single chars strings." + board_error_message = ( + "The board should be a non empty matrix of single chars strings." + ) if not isinstance(board, list) or len(board) == 0: raise ValueError(board_error_message) From 4e6572608a5a34ec64a5ca313e430b8110287eb4 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 21:17:02 +0400 Subject: [PATCH 11/14] Update backtracking/word_search.py Co-authored-by: Christian Clauss --- backtracking/word_search.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index fe63e934084a..898d8bc30af3 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -44,13 +44,13 @@ def word_exists(board: list[list[str]], word: str) -> bool: False >>> word_exists([["A"]], "A") True - >>> word_exists([["A","A","A","A","A","A"],\ - ["A","A","A","A","A","A"],\ - ["A","A","A","A","A","A"],\ - ["A","A","A","A","A","A"],\ - ["A","A","A","A","A","B"],\ - ["A","A","A","A","B","A"]],\ - "AAAAAAAAAAAAABB") + >>> word_exists([["A","A","A","A","A","A"], + ... ["A","A","A","A","A","A"], + ... ["A","A","A","A","A","A"], + ... ["A","A","A","A","A","A"], + ... ["A","A","A","A","A","B"], + ... ["A","A","A","A","B","A"]], + ... "AAAAAAAAAAAAABB") False >>> word_exists([["A"]], 123) Traceback (most recent call last): From 4e9133be7dce3a40f2fc99669585ce21e0bf32e7 Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 21:34:32 +0400 Subject: [PATCH 12/14] Update backtracking/word_search.py Co-authored-by: Christian Clauss --- backtracking/word_search.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 898d8bc30af3..d7d2bd878091 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -26,8 +26,7 @@ True Implementation notes: Use backtracking approach. -For each point need to check neigh neighbours -and try to find left suffix of the word. +At each point, check all neighbors to try to find the next letter of the word. leetcode: https://leetcode.com/problems/word-search/ From 7f765d057376eb877ea0b8d4cd7106b228c5ce4d Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 22:02:44 +0400 Subject: [PATCH 13/14] Update backtracking/word_search.py Co-authored-by: Christian Clauss --- backtracking/word_search.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index d7d2bd878091..76495d4afccc 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -131,12 +131,7 @@ def exits_word( for direction in traverts_directions: next_i = row + direction[0] next_j = column + direction[1] - if ( - next_i < 0 - or next_i >= len_board - or next_j < 0 - or next_j >= len_board_column - ): + if not (0 <= next_i < len_board and 0 <= next_j < len_board_column) continue key = get_point_key(next_i, next_j) From 295dcd1d8d45ba9e836e6540ac518c6ea0f1da9f Mon Sep 17 00:00:00 2001 From: Alexander Pantyukhin Date: Tue, 29 Nov 2022 22:07:25 +0400 Subject: [PATCH 14/14] Update backtracking/word_search.py Co-authored-by: Christian Clauss --- backtracking/word_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/word_search.py b/backtracking/word_search.py index 76495d4afccc..25d1436be36e 100644 --- a/backtracking/word_search.py +++ b/backtracking/word_search.py @@ -131,7 +131,7 @@ def exits_word( for direction in traverts_directions: next_i = row + direction[0] next_j = column + direction[1] - if not (0 <= next_i < len_board and 0 <= next_j < len_board_column) + if not (0 <= next_i < len_board and 0 <= next_j < len_board_column): continue key = get_point_key(next_i, next_j)