diff --git a/leetcode/decode_ways/README.md b/leetcode/decode_ways/README.md new file mode 100644 index 0000000..3d15f9c --- /dev/null +++ b/leetcode/decode_ways/README.md @@ -0,0 +1,42 @@ +# Decode Ways + +**Difficulty:** Medium +**Topics:** String, Dynamic Programming +**Tags:** blind-75 + +**LeetCode:** [Problem 91](https://leetcode.com/problems/decode-ways/description/) + +## Problem Description + +You have intercepted a secret message encoded as a string of numbers. The message is decoded via the mapping: `"1" -> 'A', "2" -> 'B', ..., "26" -> 'Z'`. Given a string `s` containing only digits, return the number of ways to decode it. Return `0` if it cannot be decoded. + +## Examples + +### Example 1: + +``` +Input: s = "12" +Output: 2 +Explanation: "12" could be decoded as "AB" (1 2) or "L" (12). +``` + +### Example 2: + +``` +Input: s = "226" +Output: 3 +Explanation: "226" could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6). +``` + +### Example 3: + +``` +Input: s = "06" +Output: 0 +Explanation: leading zero makes it invalid. +``` + +## Constraints + +- 1 <= s.length <= 100 +- s contains only digits and may contain leading zero(s) diff --git a/leetcode/decode_ways/__init__.py b/leetcode/decode_ways/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/decode_ways/helpers.py b/leetcode/decode_ways/helpers.py new file mode 100644 index 0000000..5b84632 --- /dev/null +++ b/leetcode/decode_ways/helpers.py @@ -0,0 +1,8 @@ +def run_num_decodings(solution_class: type, s: str): + implementation = solution_class() + return implementation.num_decodings(s) + + +def assert_num_decodings(result: int, expected: int) -> bool: + assert result == expected + return True diff --git a/leetcode/decode_ways/playground.py b/leetcode/decode_ways/playground.py new file mode 100644 index 0000000..f25ee80 --- /dev/null +++ b/leetcode/decode_ways/playground.py @@ -0,0 +1,29 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.17.3 +# kernelspec: +# display_name: leetcode-py-py3.13 +# language: python +# name: python3 +# --- + +# %% +from helpers import assert_num_decodings, run_num_decodings +from solution import Solution + +# %% +# Example test case +s = "226" +expected = 3 + +# %% +result = run_num_decodings(Solution, s) +result + +# %% +assert_num_decodings(result, expected) diff --git a/leetcode/decode_ways/solution.py b/leetcode/decode_ways/solution.py new file mode 100644 index 0000000..469f8a3 --- /dev/null +++ b/leetcode/decode_ways/solution.py @@ -0,0 +1,30 @@ +class Solution: + + # Time: O(n) + # Space: O(1) + def num_decodings(self, s: str) -> int: + if not s: + return 0 + + num_ways_two_steps_behind: int = 1 + num_ways_one_step_behind: int = 0 if s[0] == "0" else 1 + + for index in range(1, len(s)): + current_char: str = s[index] + previous_char: str = s[index - 1] + + current_num_ways: int = 0 + + if current_char != "0": + current_num_ways += num_ways_one_step_behind + + two_digit_value: int = int(previous_char + current_char) + if previous_char != "0" and 10 <= two_digit_value <= 26: + current_num_ways += num_ways_two_steps_behind + + num_ways_two_steps_behind, num_ways_one_step_behind = ( + num_ways_one_step_behind, + current_num_ways, + ) + + return num_ways_one_step_behind diff --git a/leetcode/decode_ways/test_solution.py b/leetcode/decode_ways/test_solution.py new file mode 100644 index 0000000..7992b2b --- /dev/null +++ b/leetcode/decode_ways/test_solution.py @@ -0,0 +1,34 @@ +import pytest + +from leetcode_py import logged_test + +from .helpers import assert_num_decodings, run_num_decodings +from .solution import Solution + + +class TestDecodeWays: + def setup_method(self): + self.solution = Solution() + + @logged_test + @pytest.mark.parametrize( + "s, expected", + [ + ("12", 2), + ("226", 3), + ("06", 0), + ("0", 0), + ("10", 1), + ("27", 1), + ("101", 1), + ("100", 0), + ("110", 1), + ("2101", 1), + ("2611055971756562", 4), + ("1", 1), + ("30", 0), + ], + ) + def test_num_decodings(self, s: str, expected: int): + result = run_num_decodings(Solution, s) + assert_num_decodings(result, expected) diff --git a/leetcode/set_matrix_zeroes/README.md b/leetcode/set_matrix_zeroes/README.md new file mode 100644 index 0000000..55d73c1 --- /dev/null +++ b/leetcode/set_matrix_zeroes/README.md @@ -0,0 +1,40 @@ +# Set Matrix Zeroes + +**Difficulty:** Medium +**Topics:** Array, Hash Table, Matrix +**Tags:** blind-75 + +**LeetCode:** [Problem 73](https://leetcode.com/problems/set-matrix-zeroes/description/) + +## Problem Description + +Given an `m x n` integer matrix `matrix`, if an element is `0`, set its entire row and column to `0`'s. You must do it in place. + +## Examples + +### Example 1: + +![Example 1](https://assets.leetcode.com/uploads/2020/08/17/mat1.jpg) + +``` +Input: matrix = [[1,1,1],[1,0,1],[1,1,1]] +Output: [[1,0,1],[0,0,0],[1,0,1]] +``` + +### Example 2: + +![Example 2](https://assets.leetcode.com/uploads/2020/08/17/mat2.jpg) + +``` +Input: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] +Output: [[0,0,0,0],[0,4,5,0],[0,3,1,0]] +``` + +## Constraints + +- m == matrix.length +- n == matrix[i].length +- 1 <= m, n <= 200 +- -2^31 <= matrix[i][j] <= 2^31 - 1 + +- Follow up: Could you devise a constant space solution? diff --git a/leetcode/set_matrix_zeroes/__init__.py b/leetcode/set_matrix_zeroes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/set_matrix_zeroes/helpers.py b/leetcode/set_matrix_zeroes/helpers.py new file mode 100644 index 0000000..b1627e3 --- /dev/null +++ b/leetcode/set_matrix_zeroes/helpers.py @@ -0,0 +1,12 @@ +def run_set_zeroes(solution_class: type, matrix: list[list[int]]): + import copy + + matrix_copy = copy.deepcopy(matrix) + implementation = solution_class() + implementation.set_zeroes(matrix_copy) + return matrix_copy + + +def assert_set_zeroes(result: list[list[int]], expected: list[list[int]]) -> bool: + assert result == expected + return True diff --git a/leetcode/set_matrix_zeroes/playground.py b/leetcode/set_matrix_zeroes/playground.py new file mode 100644 index 0000000..e2db3ab --- /dev/null +++ b/leetcode/set_matrix_zeroes/playground.py @@ -0,0 +1,29 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.17.3 +# kernelspec: +# display_name: leetcode-py-py3.13 +# language: python +# name: python3 +# --- + +# %% +from helpers import assert_set_zeroes, run_set_zeroes +from solution import Solution + +# %% +# Example test case +matrix = [[1, 1, 1], [1, 0, 1], [1, 1, 1]] +expected = [[1, 0, 1], [0, 0, 0], [1, 0, 1]] + +# %% +result = run_set_zeroes(Solution, matrix) +result + +# %% +assert_set_zeroes(result, expected) diff --git a/leetcode/set_matrix_zeroes/solution.py b/leetcode/set_matrix_zeroes/solution.py new file mode 100644 index 0000000..79d4996 --- /dev/null +++ b/leetcode/set_matrix_zeroes/solution.py @@ -0,0 +1,29 @@ +class Solution: + + # Time: O(m * n) + # Space: O(1) + def set_zeroes(self, matrix: list[list[int]]) -> None: + row_count: int = len(matrix) + col_count: int = len(matrix[0]) if row_count > 0 else 0 + + first_row_has_zero: bool = any(matrix[0][col_index] == 0 for col_index in range(col_count)) + first_col_has_zero: bool = any(matrix[row_index][0] == 0 for row_index in range(row_count)) + + for row_index in range(1, row_count): + for col_index in range(1, col_count): + if matrix[row_index][col_index] == 0: + matrix[row_index][0] = 0 + matrix[0][col_index] = 0 + + for row_index in range(1, row_count): + for col_index in range(1, col_count): + if matrix[row_index][0] == 0 or matrix[0][col_index] == 0: + matrix[row_index][col_index] = 0 + + if first_row_has_zero: + for col_index in range(col_count): + matrix[0][col_index] = 0 + + if first_col_has_zero: + for row_index in range(row_count): + matrix[row_index][0] = 0 diff --git a/leetcode/set_matrix_zeroes/test_solution.py b/leetcode/set_matrix_zeroes/test_solution.py new file mode 100644 index 0000000..d352403 --- /dev/null +++ b/leetcode/set_matrix_zeroes/test_solution.py @@ -0,0 +1,42 @@ +import pytest + +from leetcode_py import logged_test + +from .helpers import assert_set_zeroes, run_set_zeroes +from .solution import Solution + + +class TestSetMatrixZeroes: + def setup_method(self): + self.solution = Solution() + + @logged_test + @pytest.mark.parametrize( + "matrix, expected", + [ + ([[1, 1, 1], [1, 0, 1], [1, 1, 1]], [[1, 0, 1], [0, 0, 0], [1, 0, 1]]), + ( + [[0, 1, 2, 0], [3, 4, 5, 2], [1, 3, 1, 5]], + [[0, 0, 0, 0], [0, 4, 5, 0], [0, 3, 1, 0]], + ), + ([[1]], [[1]]), + ([[0]], [[0]]), + ([[1, 0]], [[0, 0]]), + ([[0, 1]], [[0, 0]]), + ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]]), + ([[1, 2, 0], [4, 5, 6], [7, 8, 9]], [[0, 0, 0], [4, 5, 0], [7, 8, 0]]), + ([[0, 2, 3], [4, 5, 6], [7, 8, 9]], [[0, 0, 0], [0, 5, 6], [0, 8, 9]]), + ([[1, 2, 3], [4, 0, 6], [7, 8, 0]], [[1, 0, 0], [0, 0, 0], [0, 0, 0]]), + ([[1, 2], [0, 4], [5, 6]], [[0, 2], [0, 0], [0, 6]]), + ([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]), + ([[0, 2, 0], [4, 5, 6]], [[0, 0, 0], [0, 5, 0]]), + ([[-1, 2, 3], [4, 0, -6]], [[-1, 0, 3], [0, 0, 0]]), + ( + [[1, 2, 3, 4], [0, 5, 0, 7], [8, 9, 10, 11]], + [[0, 2, 0, 4], [0, 0, 0, 0], [0, 9, 0, 11]], + ), + ], + ) + def test_set_zeroes(self, matrix: list[list[int]], expected: list[list[int]]): + result = run_set_zeroes(Solution, matrix) + assert_set_zeroes(result, expected) diff --git a/leetcode_py/cli/resources/leetcode/json/problems/decode_ways.json b/leetcode_py/cli/resources/leetcode/json/problems/decode_ways.json new file mode 100644 index 0000000..691a4b6 --- /dev/null +++ b/leetcode_py/cli/resources/leetcode/json/problems/decode_ways.json @@ -0,0 +1,67 @@ +{ + "problem_name": "decode_ways", + "solution_class_name": "Solution", + "problem_number": "91", + "problem_title": "Decode Ways", + "difficulty": "Medium", + "topics": "String, Dynamic Programming", + "_tags": { "list": ["blind-75"] }, + "readme_description": "You have intercepted a secret message encoded as a string of numbers. The message is decoded via the mapping: `\"1\" -> 'A', \"2\" -> 'B', ..., \"26\" -> 'Z'`. Given a string `s` containing only digits, return the number of ways to decode it. Return `0` if it cannot be decoded.", + "_readme_examples": { + "list": [ + { + "content": "```\nInput: s = \"12\"\nOutput: 2\nExplanation: \"12\" could be decoded as \"AB\" (1 2) or \"L\" (12).\n```" + }, + { + "content": "```\nInput: s = \"226\"\nOutput: 3\nExplanation: \"226\" could be decoded as \"BZ\" (2 26), \"VF\" (22 6), or \"BBF\" (2 2 6).\n```" + }, + { + "content": "```\nInput: s = \"06\"\nOutput: 0\nExplanation: leading zero makes it invalid.\n```" + } + ] + }, + "readme_constraints": "- 1 <= s.length <= 100\n- s contains only digits and may contain leading zero(s)", + "readme_additional": "", + "helpers_imports": "", + "helpers_content": "", + "helpers_run_name": "num_decodings", + "helpers_run_signature": "(solution_class: type, s: str)", + "helpers_run_body": " implementation = solution_class()\n return implementation.num_decodings(s)", + "helpers_assert_name": "num_decodings", + "helpers_assert_signature": "(result: int, expected: int) -> bool", + "helpers_assert_body": " assert result == expected\n return True", + "solution_imports": "", + "solution_contents": "", + "solution_class_content": "", + "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_num_decodings, run_num_decodings\nfrom .solution import Solution", + "test_content": "", + "test_class_name": "DecodeWays", + "test_class_content": " def setup_method(self):\n self.solution = Solution()", + "_solution_methods": { + "list": [ + { + "name": "num_decodings", + "signature": "(self, s: str) -> int", + "body": " # TODO: Implement num_decodings\n return 0" + } + ] + }, + "_test_helper_methods": { + "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }] + }, + "_test_methods": { + "list": [ + { + "name": "test_num_decodings", + "signature": "(self, s: str, expected: int)", + "parametrize": "s, expected", + "test_cases": "[('12', 2), ('226', 3), ('06', 0), ('0', 0), ('10', 1), ('27', 1), ('101', 1), ('100', 0), ('110', 1), ('2101', 1), ('2611055971756562', 4), ('1', 1), ('30', 0)]", + "body": " result = run_num_decodings(Solution, s)\n assert_num_decodings(result, expected)" + } + ] + }, + "playground_imports": "from helpers import run_num_decodings, assert_num_decodings\nfrom solution import Solution", + "playground_setup": "# Example test case\ns = '226'\nexpected = 3", + "playground_run": "result = run_num_decodings(Solution, s)\nresult", + "playground_assert": "assert_num_decodings(result, expected)" +} diff --git a/leetcode_py/cli/resources/leetcode/json/problems/set_matrix_zeroes.json b/leetcode_py/cli/resources/leetcode/json/problems/set_matrix_zeroes.json new file mode 100644 index 0000000..b642d2e --- /dev/null +++ b/leetcode_py/cli/resources/leetcode/json/problems/set_matrix_zeroes.json @@ -0,0 +1,64 @@ +{ + "problem_name": "set_matrix_zeroes", + "solution_class_name": "Solution", + "problem_number": "73", + "problem_title": "Set Matrix Zeroes", + "difficulty": "Medium", + "topics": "Array, Hash Table, Matrix", + "_tags": { "list": ["blind-75"] }, + "readme_description": "Given an `m x n` integer matrix `matrix`, if an element is `0`, set its entire row and column to `0`'s. You must do it in place.", + "_readme_examples": { + "list": [ + { + "content": "![Example 1](https://assets.leetcode.com/uploads/2020/08/17/mat1.jpg)\n\n```\nInput: matrix = [[1,1,1],[1,0,1],[1,1,1]]\nOutput: [[1,0,1],[0,0,0],[1,0,1]]\n```" + }, + { + "content": "![Example 2](https://assets.leetcode.com/uploads/2020/08/17/mat2.jpg)\n\n```\nInput: matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]\nOutput: [[0,0,0,0],[0,4,5,0],[0,3,1,0]]\n```" + } + ] + }, + "readme_constraints": "- m == matrix.length\n- n == matrix[i].length\n- 1 <= m, n <= 200\n- -2^31 <= matrix[i][j] <= 2^31 - 1", + "readme_additional": "- Follow up: Could you devise a constant space solution?", + "helpers_imports": "", + "helpers_content": "", + "helpers_run_name": "set_zeroes", + "helpers_run_signature": "(solution_class: type, matrix: list[list[int]])", + "helpers_run_body": " import copy\n matrix_copy = copy.deepcopy(matrix)\n implementation = solution_class()\n implementation.set_zeroes(matrix_copy)\n return matrix_copy", + "helpers_assert_name": "set_zeroes", + "helpers_assert_signature": "(result: list[list[int]], expected: list[list[int]]) -> bool", + "helpers_assert_body": " assert result == expected\n return True", + "solution_imports": "", + "solution_contents": "", + "solution_class_content": "", + "test_imports": "import pytest\nfrom leetcode_py import logged_test\nfrom .helpers import assert_set_zeroes, run_set_zeroes\nfrom .solution import Solution", + "test_content": "", + "test_class_name": "SetMatrixZeroes", + "test_class_content": " def setup_method(self):\n self.solution = Solution()", + "_solution_methods": { + "list": [ + { + "name": "set_zeroes", + "signature": "(self, matrix: list[list[int]]) -> None", + "body": " # TODO: Implement set_zeroes\n pass" + } + ] + }, + "_test_helper_methods": { + "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }] + }, + "_test_methods": { + "list": [ + { + "name": "test_set_zeroes", + "signature": "(self, matrix: list[list[int]], expected: list[list[int]])", + "parametrize": "matrix, expected", + "test_cases": "[([[1,1,1],[1,0,1],[1,1,1]], [[1,0,1],[0,0,0],[1,0,1]]), ([[0,1,2,0],[3,4,5,2],[1,3,1,5]], [[0,0,0,0],[0,4,5,0],[0,3,1,0]]), ([[1]], [[1]]), ([[0]], [[0]]), ([[1,0]], [[0,0]]), ([[0,1]], [[0,0]]), ([[1,2,3],[4,5,6],[7,8,9]], [[1,2,3],[4,5,6],[7,8,9]]), ([[1,2,0],[4,5,6],[7,8,9]], [[0,0,0],[4,5,0],[7,8,0]]), ([[0,2,3],[4,5,6],[7,8,9]], [[0,0,0],[0,5,6],[0,8,9]]), ([[1,2,3],[4,0,6],[7,8,0]], [[1,0,0],[0,0,0],[0,0,0]]), ([[1,2],[0,4],[5,6]], [[0,2],[0,0],[0,6]]), ([[1,2,3],[4,5,6]], [[1,2,3],[4,5,6]]), ([[0,2,0],[4,5,6]], [[0,0,0],[0,5,0]]), ([[-1,2,3],[4,0,-6]], [[-1,0,3],[0,0,0]]), ([[1,2,3,4],[0,5,0,7],[8,9,10,11]], [[0,2,0,4],[0,0,0,0],[0,9,0,11]])]", + "body": " result = run_set_zeroes(Solution, matrix)\n assert_set_zeroes(result, expected)" + } + ] + }, + "playground_imports": "from helpers import run_set_zeroes, assert_set_zeroes\nfrom solution import Solution", + "playground_setup": "# Example test case\nmatrix = [[1,1,1],[1,0,1],[1,1,1]]\nexpected = [[1,0,1],[0,0,0],[1,0,1]]", + "playground_run": "result = run_set_zeroes(Solution, matrix)\nresult", + "playground_assert": "assert_set_zeroes(result, expected)" +} diff --git a/leetcode_py/cli/resources/leetcode/json/tags.json5 b/leetcode_py/cli/resources/leetcode/json/tags.json5 index a4b1e6d..90b2c59 100644 --- a/leetcode_py/cli/resources/leetcode/json/tags.json5 +++ b/leetcode_py/cli/resources/leetcode/json/tags.json5 @@ -120,6 +120,7 @@ "container_with_most_water", "contains_duplicate", "course_schedule", + "decode_ways", "design_add_and_search_words_data_structure", "find_median_from_data_stream", "group_anagrams", @@ -150,6 +151,7 @@ "rotate_image", "search_in_rotated_sorted_array", "serialize_and_deserialize_binary_tree", + "set_matrix_zeroes", "spiral_matrix", "three_sum", "two_sum", diff --git a/poetry.lock b/poetry.lock index 9569626..38bbb65 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "anytree" @@ -2296,14 +2296,14 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" description = "Measures the displayed width of unicode strings in a terminal" optional = false -python-versions = "*" +python-versions = ">=3.6" groups = ["dev"] files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, + {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, + {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, ] [[package]]