diff --git a/.amazonq/plans/algo_master_75_tuples.py b/.amazonq/plans/algo_master_75_tuples.py new file mode 100644 index 0000000..255422e --- /dev/null +++ b/.amazonq/plans/algo_master_75_tuples.py @@ -0,0 +1,77 @@ +algo_master_75_tuples = [ + (2, "Add Two Numbers"), + (3, "Longest Substring Without Repeating Characters"), + (4, "Median of Two Sorted Arrays"), + (7, "Reverse Integer"), + (11, "Container With Most Water"), + (15, "3Sum"), + (19, "Remove Nth Node From End of List"), + (20, "Valid Parentheses"), + (23, "Merge k Sorted Lists"), + (24, "Swap Nodes in Pairs"), + (25, "Reverse Nodes in k-Group"), + (33, "Search in Rotated Sorted Array"), + (34, "Find First and Last Position of Element in Sorted Array"), + (41, "First Missing Positive"), + (42, "Trapping Rain Water"), + (45, "Jump Game II"), + (46, "Permutations"), + (48, "Rotate Image"), + (49, "Group Anagrams"), + (53, "Maximum Subarray"), + (54, "Spiral Matrix"), + (56, "Merge Intervals"), + (64, "Minimum Path Sum"), + (75, "Sort Colors"), + (76, "Minimum Window Substring"), + (78, "Subsets"), + (84, "Largest Rectangle in Histogram"), + (94, "Binary Tree Inorder Traversal"), + (98, "Validate Binary Search Tree"), + (102, "Binary Tree Level Order Traversal"), + (124, "Binary Tree Maximum Path Sum"), + (127, "Word Ladder"), + (128, "Longest Consecutive Sequence"), + (133, "Clone Graph"), + (139, "Word Break"), + (142, "Linked List Cycle II"), + (144, "Binary Tree Preorder Traversal"), + (145, "Binary Tree Postorder Traversal"), + (146, "LRU Cache"), + (149, "Max Points on a Line"), + (151, "Reverse Words in a String"), + (155, "Min Stack"), + (169, "Majority Element"), + (199, "Binary Tree Right Side View"), + (200, "Number of Islands"), + (208, "Implement Trie (Prefix Tree)"), + (210, "Course Schedule II"), + (212, "Word Search II"), + (213, "House Robber II"), + (230, "Kth Smallest Element in a BST"), + (236, "Lowest Common Ancestor of a Binary Tree"), + (238, "Product of Array Except Self"), + (239, "Sliding Window Maximum"), + (260, "Single Number III"), + (295, "Find Median from Data Stream"), + (297, "Serialize and Deserialize Binary Tree"), + (300, "Longest Increasing Subsequence"), + (322, "Coin Change"), + (329, "Longest Increasing Path in a Matrix"), + (338, "Counting Bits"), + (347, "Top K Frequent Elements"), + (392, "Is Subsequence"), + (416, "Partition Equal Subset Sum"), + (435, "Non-overlapping Intervals"), + (437, "Path Sum III"), + (438, "Find All Anagrams in a String"), + (547, "Number of Provinces"), + (560, "Subarray Sum Equals K"), + (567, "Permutation in String"), + (729, "My Calendar I"), + (785, "Is Graph Bipartite?"), + (787, "Cheapest Flights Within K Stops"), + (994, "Rotting Oranges"), + (1143, "Longest Common Subsequence"), + (1584, "Min Cost to Connect All Points"), +] diff --git a/.amazonq/plans/blind_75_tuples.py b/.amazonq/plans/blind_75_tuples.py new file mode 100644 index 0000000..bf6af06 --- /dev/null +++ b/.amazonq/plans/blind_75_tuples.py @@ -0,0 +1,77 @@ +blind_75_tuples = [ + (1, "Two Sum"), + (3, "Longest Substring Without Repeating Characters"), + (5, "Longest Palindromic Substring"), + (11, "Container With Most Water"), + (15, "3Sum"), + (19, "Remove Nth Node From End of List"), + (20, "Valid Parentheses"), + (21, "Merge Two Sorted Lists"), + (23, "Merge k Sorted Lists"), + (33, "Search in Rotated Sorted Array"), + (39, "Combination Sum"), + (48, "Rotate Image"), + (49, "Group Anagrams"), + (53, "Maximum Subarray"), + (54, "Spiral Matrix"), + (55, "Jump Game"), + (56, "Merge Intervals"), + (57, "Insert Interval"), + (62, "Unique Paths"), + (70, "Climbing Stairs"), + (73, "Set Matrix Zeroes"), + (76, "Minimum Window Substring"), + (79, "Word Search"), + (91, "Decode Ways"), + (98, "Validate Binary Search Tree"), + (100, "Same Tree"), + (102, "Binary Tree Level Order Traversal"), + (104, "Maximum Depth of Binary Tree"), + (105, "Construct Binary Tree from Preorder and Inorder Traversal"), + (121, "Best Time to Buy and Sell Stock"), + (124, "Binary Tree Maximum Path Sum"), + (125, "Valid Palindrome"), + (128, "Longest Consecutive Sequence"), + (133, "Clone Graph"), + (139, "Word Break"), + (141, "Linked List Cycle"), + (143, "Reorder List"), + (152, "Maximum Product Subarray"), + (153, "Find Minimum in Rotated Sorted Array"), + (190, "Reverse Bits"), + (191, "Number of 1 Bits"), + (198, "House Robber"), + (200, "Number of Islands"), + (206, "Reverse Linked List"), + (207, "Course Schedule"), + (208, "Implement Trie (Prefix Tree)"), + (211, "Design Add and Search Words Data Structure"), + (212, "Word Search II"), + (213, "House Robber II"), + (217, "Contains Duplicate"), + (226, "Invert Binary Tree"), + (230, "Kth Smallest Element in a BST"), + (235, "Lowest Common Ancestor of a Binary Search Tree"), + (238, "Product of Array Except Self"), + (242, "Valid Anagram"), + (252, "Meeting Rooms"), + (253, "Meeting Rooms II"), + (261, "Graph Valid Tree"), + (268, "Missing Number"), + (269, "Alien Dictionary"), + (271, "Encode and Decode Strings"), + (295, "Find Median from Data Stream"), + (297, "Serialize and Deserialize Binary Tree"), + (300, "Longest Increasing Subsequence"), + (322, "Coin Change"), + (323, "Number of Connected Components in an Undirected Graph"), + (338, "Counting Bits"), + (347, "Top K Frequent Elements"), + (371, "Sum of Two Integers"), + (417, "Pacific Atlantic Water Flow"), + (424, "Longest Repeating Character Replacement"), + (435, "Non-overlapping Intervals"), + (572, "Subtree of Another Tree"), + (647, "Palindromic Substrings"), + (1143, "Longest Common Subsequence"), +] diff --git a/.amazonq/plans/check_problem_lists.py b/.amazonq/plans/check_problem_lists.py new file mode 100644 index 0000000..430c5fe --- /dev/null +++ b/.amazonq/plans/check_problem_lists.py @@ -0,0 +1,66 @@ +# TODO: temporary use only while completing ongoing list + +import json +import sys +from pathlib import Path + +# Import the tuple lists +sys.path.append(str(Path(__file__).parent.parent.parent)) +from algo_master_75_tuples import algo_master_75_tuples +from blind_75_tuples import blind_75_tuples +from neetcode_150_tuples import neetcode_150_tuples + + +def get_existing_problems(): + """Get problem numbers from existing JSON files.""" + json_dir = Path(__file__).parent.parent.parent / "leetcode_py/cli/resources/leetcode/json/problems" + existing_problems = set() + + for json_file in json_dir.glob("*.json"): + try: + with open(json_file, "r") as f: + data = json.load(f) + problem_number = int(data.get("problem_number", 0)) + if problem_number > 0: + existing_problems.add(problem_number) + except (json.JSONDecodeError, ValueError, KeyError): + continue + + return existing_problems + + +def check_problem_list(name, problem_tuples, existing_problems): + """Check how many problems from a list are available.""" + problem_numbers = {num for num, _ in problem_tuples} + have = problem_numbers & existing_problems + missing = problem_numbers - existing_problems + + print(f"\n=== {name} ===") + print(f"Total problems: {len(problem_numbers)}") + print(f"Problems you have: {len(have)} ({len(have)/len(problem_numbers)*100:.1f}%)") + print(f"Problems missing: {len(missing)} ({len(missing)/len(problem_numbers)*100:.1f}%)") + + if missing: + print(f"Missing problems: {sorted(missing)}") + + return have, missing + + +def main(): + existing = get_existing_problems() + print(f"Total existing problems in JSON: {len(existing)}") + + # Check each list + blind_have, blind_missing = check_problem_list("BLIND 75", blind_75_tuples, existing) + neetcode_have, neetcode_missing = check_problem_list("NEETCODE 150", neetcode_150_tuples, existing) + algo_have, algo_missing = check_problem_list("ALGO MASTER 75", algo_master_75_tuples, existing) + + # Summary + print("\n=== SUMMARY ===") + print(f"Blind 75: {len(blind_have)}/75 ({len(blind_have)/75*100:.1f}%)") + print(f"NeetCode 150: {len(neetcode_have)}/150 ({len(neetcode_have)/150*100:.1f}%)") + print(f"Algo Master 75: {len(algo_have)}/75 ({len(algo_have)/75*100:.1f}%)") + + +if __name__ == "__main__": + main() diff --git a/.amazonq/plans/export_tags.py b/.amazonq/plans/export_tags.py new file mode 100644 index 0000000..9b3db10 --- /dev/null +++ b/.amazonq/plans/export_tags.py @@ -0,0 +1,81 @@ +# TODO: temporary use only while completing ongoing list + +import json +import sys +from pathlib import Path + +# Import the tuple lists +sys.path.append(str(Path(__file__).parent.parent.parent)) +from algo_master_75_tuples import algo_master_75_tuples +from neetcode_150_tuples import neetcode_150_tuples + + +def get_existing_problems(): + """Get problem numbers and names from existing JSON files.""" + json_dir = Path(__file__).parent.parent.parent / "leetcode_py/cli/resources/leetcode/json/problems" + existing_problems = {} + + for json_file in json_dir.glob("*.json"): + try: + with open(json_file, "r") as f: + data = json.load(f) + problem_number = int(data.get("problem_number", 0)) + if problem_number > 0: + existing_problems[problem_number] = json_file.stem + except (json.JSONDecodeError, ValueError, KeyError): + continue + + return existing_problems + + +def get_tag_problems(problem_tuples, existing_problems): + """Get problem names for existing problems from a tuple list.""" + problem_names = [] + for num, _ in problem_tuples: + if num in existing_problems: + problem_names.append(existing_problems[num]) + return sorted(problem_names) + + +def export_tags(): + """Export tags.json5 format for the three problem lists.""" + existing = get_existing_problems() + + # Get problem names for each list + neetcode_150_names = get_tag_problems(neetcode_150_tuples, existing) + algo_master_75_names = get_tag_problems(algo_master_75_tuples, existing) + + # Generate tags.json5 content + content = """{\n""" + + # NeetCode 150 + content += f" // NeetCode 150 - {len(neetcode_150_names)} problems\n" + content += ' "neetcode-150": [\n' + for name in neetcode_150_names: + content += f' "{name}",\n' + content += " ],\n\n" + + # Algo Master 75 + content += f" // Algo Master 75 - {len(algo_master_75_names)} problems\n" + content += ' "algo-master-75": [\n' + for name in algo_master_75_names: + content += f' "{name}",\n' + content += " ],\n\n" + + # Test tag + content += " // Test tag for development and testing\n" + content += ' test: ["binary_search", "two_sum", "valid_palindrome"],\n' + content += "}\n" + + # Write to file + output_file = Path(__file__).parent / "new_tags.json5" + with open(output_file, "w") as f: + f.write(content) + + print(f"Exported tags to {output_file}") + print(f"NeetCode 150: {len(neetcode_150_names)} problems") + print(f"Algo Master 75: {len(algo_master_75_names)} problems") + + +if __name__ == "__main__": + export_tags() diff --git a/.amazonq/plans/neetcode_150_tuples.py b/.amazonq/plans/neetcode_150_tuples.py new file mode 100644 index 0000000..51753de --- /dev/null +++ b/.amazonq/plans/neetcode_150_tuples.py @@ -0,0 +1,152 @@ +neetcode_150_tuples = [ + (1, "Two Sum"), + (2, "Add Two Numbers"), + (3, "Longest Substring Without Repeating Characters"), + (4, "Median of Two Sorted Arrays"), + (5, "Longest Palindromic Substring"), + (7, "Reverse Integer"), + (10, "Regular Expression Matching"), + (11, "Container With Most Water"), + (15, "3Sum"), + (17, "Letter Combinations of a Phone Number"), + (19, "Remove Nth Node From End of List"), + (20, "Valid Parentheses"), + (21, "Merge Two Sorted Lists"), + (22, "Generate Parentheses"), + (23, "Merge K Sorted Lists"), + (25, "Reverse Nodes In K Group"), + (33, "Search In Rotated Sorted Array"), + (36, "Valid Sudoku"), + (39, "Combination Sum"), + (40, "Combination Sum II"), + (42, "Trapping Rain Water"), + (43, "Multiply Strings"), + (45, "Jump Game II"), + (46, "Permutations"), + (48, "Rotate Image"), + (49, "Group Anagrams"), + (50, "Pow(x, n)"), + (51, "N Queens"), + (53, "Maximum Subarray"), + (54, "Spiral Matrix"), + (55, "Jump Game"), + (56, "Merge Intervals"), + (57, "Insert Interval"), + (62, "Unique Paths"), + (66, "Plus One"), + (70, "Climbing Stairs"), + (72, "Edit Distance"), + (73, "Set Matrix Zeroes"), + (74, "Search a 2D Matrix"), + (76, "Minimum Window Substring"), + (78, "Subsets"), + (79, "Word Search"), + (84, "Largest Rectangle In Histogram"), + (90, "Subsets II"), + (91, "Decode Ways"), + (97, "Interleaving String"), + (98, "Validate Binary Search Tree"), + (100, "Same Tree"), + (102, "Binary Tree Level Order Traversal"), + (104, "Maximum Depth of Binary Tree"), + (105, "Construct Binary Tree From Preorder And Inorder Traversal"), + (110, "Balanced Binary Tree"), + (115, "Distinct Subsequences"), + (121, "Best Time to Buy And Sell Stock"), + (124, "Binary Tree Maximum Path Sum"), + (125, "Valid Palindrome"), + (127, "Word Ladder"), + (128, "Longest Consecutive Sequence"), + (130, "Surrounded Regions"), + (131, "Palindrome Partitioning"), + (133, "Clone Graph"), + (134, "Gas Station"), + (136, "Single Number"), + (138, "Copy List With Random Pointer"), + (139, "Word Break"), + (141, "Linked List Cycle"), + (143, "Reorder List"), + (146, "LRU Cache"), + (150, "Evaluate Reverse Polish Notation"), + (152, "Maximum Product Subarray"), + (153, "Find Minimum In Rotated Sorted Array"), + (155, "Min Stack"), + (167, "Two Sum II Input Array Is Sorted"), + (190, "Reverse Bits"), + (191, "Number of 1 Bits"), + (198, "House Robber"), + (199, "Binary Tree Right Side View"), + (200, "Number of Islands"), + (202, "Happy Number"), + (206, "Reverse Linked List"), + (207, "Course Schedule"), + (208, "Implement Trie Prefix Tree"), + (210, "Course Schedule II"), + (211, "Design Add And Search Words Data Structure"), + (212, "Word Search II"), + (213, "House Robber II"), + (215, "Kth Largest Element In An Array"), + (217, "Contains Duplicate"), + (226, "Invert Binary Tree"), + (230, "Kth Smallest Element In a Bst"), + (235, "Lowest Common Ancestor of a Binary Search Tree"), + (238, "Product of Array Except Self"), + (239, "Sliding Window Maximum"), + (242, "Valid Anagram"), + (252, "Meeting Rooms"), + (253, "Meeting Rooms II"), + (261, "Graph Valid Tree"), + (268, "Missing Number"), + (269, "Alien Dictionary"), + (271, "Encode and Decode Strings"), + (286, "Walls And Gates"), + (287, "Find The Duplicate Number"), + (295, "Find Median From Data Stream"), + (297, "Serialize And Deserialize Binary Tree"), + (300, "Longest Increasing Subsequence"), + (309, "Best Time to Buy And Sell Stock With Cooldown"), + (312, "Burst Balloons"), + (322, "Coin Change"), + (323, "Number of Connected Components In An Undirected Graph"), + (329, "Longest Increasing Path In a Matrix"), + (332, "Reconstruct Itinerary"), + (338, "Counting Bits"), + (347, "Top K Frequent Elements"), + (355, "Design Twitter"), + (371, "Sum of Two Integers"), + (416, "Partition Equal Subset Sum"), + (417, "Pacific Atlantic Water Flow"), + (424, "Longest Repeating Character Replacement"), + (435, "Non Overlapping Intervals"), + (494, "Target Sum"), + (518, "Coin Change II"), + (543, "Diameter of Binary Tree"), + (567, "Permutation In String"), + (572, "Subtree of Another Tree"), + (621, "Task Scheduler"), + (647, "Palindromic Substrings"), + (678, "Valid Parenthesis String"), + (684, "Redundant Connection"), + (695, "Max Area of Island"), + (703, "Kth Largest Element In a Stream"), + (704, "Binary Search"), + (739, "Daily Temperatures"), + (743, "Network Delay Time"), + (746, "Min Cost Climbing Stairs"), + (763, "Partition Labels"), + (778, "Swim In Rising Water"), + (787, "Cheapest Flights Within K Stops"), + (846, "Hand of Straights"), + (853, "Car Fleet"), + (875, "Koko Eating Bananas"), + (973, "K Closest Points to Origin"), + (981, "Time Based Key Value Store"), + (994, "Rotting Oranges"), + (1046, "Last Stone Weight"), + (1143, "Longest Common Subsequence"), + (1448, "Count Good Nodes In Binary Tree"), + (1584, "Min Cost to Connect All Points"), + (1851, "Minimum Interval to Include Each Query"), + (1899, "Merge Triplets to Form Target Triplet"), + (2013, "Detect Squares"), +] diff --git a/leetcode_py/cli/commands/gen.py b/leetcode_py/cli/commands/gen.py index 067a97d..688feb2 100644 --- a/leetcode_py/cli/commands/gen.py +++ b/leetcode_py/cli/commands/gen.py @@ -12,6 +12,7 @@ get_problem_json_path, ) from ..utils.resources import get_template_path +from ..utils.tag_helpers import get_available_tags_help def _get_problem_difficulty(problem_name: str) -> str | None: @@ -112,7 +113,12 @@ def generate( problem_slugs: list[str] = typer.Option( [], "-s", "--problem-slug", help="Problem slug(s) (use multiple -s flags)" ), - problem_tag: str | None = typer.Option(None, "-t", "--problem-tag", help="Problem tag (bulk)"), + problem_tag: str | None = typer.Option( + None, + "-t", + "--problem-tag", + help="Problem tag (bulk). Available tags: " + get_available_tags_help(), + ), difficulty: str | None = typer.Option( None, "-d", "--difficulty", help="Filter by difficulty (Easy/Medium/Hard)" ), diff --git a/leetcode_py/cli/commands/list.py b/leetcode_py/cli/commands/list.py index 6b8c3e4..4ec7ea0 100644 --- a/leetcode_py/cli/commands/list.py +++ b/leetcode_py/cli/commands/list.py @@ -5,12 +5,15 @@ from rich.table import Table from ..utils.problem_finder import find_problems_by_tag, get_all_problems, get_tags_for_problem +from ..utils.tag_helpers import get_available_tags_help console = Console() def list_problems( - tag: str | None = typer.Option(None, "-t", "--tag", help="Filter by tag (e.g., 'grind-75')"), + tag: str | None = typer.Option( + None, "-t", "--tag", help="Filter by tag. Available tags: " + get_available_tags_help() + ), difficulty: str | None = typer.Option( None, "-d", "--difficulty", help="Filter by difficulty (Easy/Medium/Hard)" ), diff --git a/leetcode_py/cli/resources/leetcode/json/tags.json5 b/leetcode_py/cli/resources/leetcode/json/tags.json5 index 2b377c8..7a2b12e 100644 --- a/leetcode_py/cli/resources/leetcode/json/tags.json5 +++ b/leetcode_py/cli/resources/leetcode/json/tags.json5 @@ -1,5 +1,5 @@ { - // Grind 75 - extracted from actual problem JSON files + // Grind 75 - (completed) "grind-75": [ "accounts_merge", "add_binary", @@ -78,6 +78,7 @@ "zero_one_matrix", ], + // Grind - (on-going) grind: [ { tag: "grind-75" }, "contiguous_array", @@ -105,6 +106,177 @@ "valid_sudoku", ], - // Test tag for development and testing - test: ["binary_search", "two_sum", "valid_palindrome"], + // Blind 75 - (on-going) + "blind-75": [ + "best_time_to_buy_and_sell_stock", + "binary_tree_level_order_traversal", + "climbing_stairs", + "clone_graph", + "coin_change", + "combination_sum", + "construct_binary_tree_from_preorder_and_inorder_traversal", + "container_with_most_water", + "contains_duplicate", + "course_schedule", + "design_add_and_search_words_data_structure", + "find_median_from_data_stream", + "group_anagrams", + "house_robber", + "implement_trie_prefix_tree", + "insert_interval", + "invert_binary_tree", + "kth_smallest_element_in_a_bst", + "linked_list_cycle", + "longest_consecutive_sequence", + "longest_increasing_subsequence", + "longest_palindromic_substring", + "longest_substring_without_repeating_characters", + "lowest_common_ancestor_of_a_binary_search_tree", + "maximum_depth_of_binary_tree", + "maximum_product_subarray", + "maximum_subarray", + "merge_intervals", + "merge_k_sorted_lists", + "merge_two_sorted_lists", + "minimum_window_substring", + "number_of_islands", + "pacific_atlantic_water_flow", + "product_of_array_except_self", + "remove_nth_node_from_end_of_list", + "reverse_linked_list", + "search_in_rotated_sorted_array", + "serialize_and_deserialize_binary_tree", + "spiral_matrix", + "three_sum", + "two_sum", + "unique_paths", + "valid_anagram", + "valid_palindrome", + "valid_parentheses", + "validate_binary_search_tree", + "word_break", + "word_search", + ], + + // NeetCode 150 - (ongoing) + "neetcode-150": [ + "balanced_binary_tree", + "best_time_to_buy_and_sell_stock", + "binary_search", + "binary_tree_level_order_traversal", + "binary_tree_right_side_view", + "climbing_stairs", + "clone_graph", + "coin_change", + "combination_sum", + "construct_binary_tree_from_preorder_and_inorder_traversal", + "container_with_most_water", + "contains_duplicate", + "course_schedule", + "course_schedule_ii", + "daily_temperatures", + "design_add_and_search_words_data_structure", + "diameter_of_binary_tree", + "evaluate_reverse_polish_notation", + "find_median_from_data_stream", + "find_the_duplicate_number", + "gas_station", + "group_anagrams", + "house_robber", + "implement_trie_prefix_tree", + "insert_interval", + "invert_binary_tree", + "k_closest_points_to_origin", + "kth_smallest_element_in_a_bst", + "largest_rectangle_in_histogram", + "letter_combinations_of_a_phone_number", + "linked_list_cycle", + "longest_consecutive_sequence", + "longest_increasing_subsequence", + "longest_palindromic_substring", + "longest_substring_without_repeating_characters", + "lowest_common_ancestor_of_a_binary_search_tree", + "lru_cache", + "maximum_depth_of_binary_tree", + "maximum_product_subarray", + "maximum_subarray", + "merge_intervals", + "merge_k_sorted_lists", + "merge_two_sorted_lists", + "min_stack", + "minimum_window_substring", + "number_of_islands", + "pacific_atlantic_water_flow", + "partition_equal_subset_sum", + "permutations", + "product_of_array_except_self", + "remove_nth_node_from_end_of_list", + "reverse_linked_list", + "rotting_oranges", + "search_in_rotated_sorted_array", + "serialize_and_deserialize_binary_tree", + "spiral_matrix", + "subsets", + "task_scheduler", + "three_sum", + "time_based_key_value_store", + "trapping_rain_water", + "two_sum", + "unique_paths", + "valid_anagram", + "valid_palindrome", + "valid_parentheses", + "valid_sudoku", + "validate_binary_search_tree", + "word_break", + "word_ladder", + "word_search", + ], + + // Algo Master 75 - (ongoing) + "algo-master-75": [ + "binary_tree_level_order_traversal", + "binary_tree_right_side_view", + "clone_graph", + "coin_change", + "container_with_most_water", + "course_schedule_ii", + "find_all_anagrams_in_a_string", + "find_median_from_data_stream", + "group_anagrams", + "implement_trie_prefix_tree", + "kth_smallest_element_in_a_bst", + "largest_rectangle_in_histogram", + "longest_consecutive_sequence", + "longest_increasing_subsequence", + "longest_substring_without_repeating_characters", + "lowest_common_ancestor_of_a_binary_tree", + "lru_cache", + "majority_element", + "maximum_subarray", + "merge_intervals", + "merge_k_sorted_lists", + "min_stack", + "minimum_window_substring", + "number_of_islands", + "partition_equal_subset_sum", + "permutations", + "product_of_array_except_self", + "remove_nth_node_from_end_of_list", + "rotting_oranges", + "search_in_rotated_sorted_array", + "serialize_and_deserialize_binary_tree", + "sort_colors", + "spiral_matrix", + "subsets", + "swap_nodes_in_pairs", + "three_sum", + "trapping_rain_water", + "valid_parentheses", + "validate_binary_search_tree", + "word_break", + "word_ladder", + ], + + } diff --git a/leetcode_py/cli/utils/tag_helpers.py b/leetcode_py/cli/utils/tag_helpers.py new file mode 100644 index 0000000..e8cc2d4 --- /dev/null +++ b/leetcode_py/cli/utils/tag_helpers.py @@ -0,0 +1,17 @@ +"""Helper functions for working with tags.""" + +from .resources import get_tags_path + + +def get_available_tags_help() -> str: + """Get available tags for help text.""" + try: + import json5 + + tags_file = get_tags_path() + with open(tags_file) as f: + tags_data = json5.load(f) + available_tags = list(tags_data.keys()) + return ", ".join(available_tags) + except Exception: + return "grind-75, blind-75, neetcode-150" diff --git a/tests/cli/test_gen.py b/tests/cli/test_gen.py index 6fe2728..5144d98 100644 --- a/tests/cli/test_gen.py +++ b/tests/cli/test_gen.py @@ -35,8 +35,8 @@ def test_gen_no_options(): "args", [ ["-n", "1", "-s", "two-sum"], - ["-n", "1", "-t", "test"], - ["-s", "two-sum", "-t", "test"], + ["-n", "1", "-t", "grind-75"], + ["-s", "two-sum", "-t", "grind-75"], ["-n", "1", "--all"], ], ) @@ -97,17 +97,17 @@ def test_gen_by_slugs(args, expected_problems, expected_count): def test_gen_by_tag(): with tempfile.TemporaryDirectory() as temp_dir: - result = runner.invoke(app, ["gen", "-t", "test", "-o", temp_dir, "--force"]) + result = runner.invoke(app, ["gen", "-t", "grind-75", "-o", temp_dir, "--force"]) assert result.exit_code == 0 assert "Found" in result.stdout - assert "problems with tag 'test'" in result.stdout + assert "problems with tag 'grind-75'" in result.stdout assert "Generated problem:" in result.stdout assert "successful" in result.stdout def test_gen_with_difficulty_filter(): with tempfile.TemporaryDirectory() as temp_dir: - result = runner.invoke(app, ["gen", "-t", "test", "-d", "Easy", "-o", temp_dir, "--force"]) + result = runner.invoke(app, ["gen", "-t", "grind-75", "-d", "Easy", "-o", temp_dir, "--force"]) assert result.exit_code == 0 assert "Found" in result.stdout assert "Filtered to" in result.stdout diff --git a/tests/cli/test_problem_finder.py b/tests/cli/test_problem_finder.py index d84dcfc..8b6442e 100644 --- a/tests/cli/test_problem_finder.py +++ b/tests/cli/test_problem_finder.py @@ -1,6 +1,7 @@ import pytest from leetcode_py.cli.utils.problem_finder import ( + _build_problem_tags_cache, find_problem_by_number, find_problems_by_tag, get_all_problems, @@ -25,7 +26,7 @@ def test_find_problem_by_number_not_found(): def test_find_problems_by_tag(): # Test existing tag - result = find_problems_by_tag("test") + result = find_problems_by_tag("grind-75") assert isinstance(result, list) assert len(result) > 0 @@ -78,7 +79,7 @@ def test_find_problem_by_number_parametrized(number, expected): "tag,should_exist", [ ("grind-75", True), - ("test", True), + ("grind-75", True), ("nonexistent", False), ], ) @@ -101,6 +102,30 @@ def test_problem_finder_consistency(): assert problem in all_problems # Test that problems found by tag are in all_problems - test_problems = find_problems_by_tag("test") - for problem in test_problems: + grind_problems = find_problems_by_tag("grind-75") + for problem in grind_problems: assert problem in all_problems + + +def test_build_problem_tags_cache_with_real_tags(): + result = _build_problem_tags_cache() + + # Test that grind tag includes both grind-75 problems and daily_temperatures + assert "daily_temperatures" in result + assert "grind" in result["daily_temperatures"] + + # Test that grind-75 problems also get grind tag + assert "two_sum" in result + assert "grind-75" in result["two_sum"] + assert "grind" in result["two_sum"] + + +def test_get_tags_for_problem_extended(): + # Test daily_temperatures has grind tag + tags = get_tags_for_problem("daily_temperatures") + assert "grind" in tags + + # Test grind-75 problem has both tags + tags = get_tags_for_problem("two_sum") + assert "grind-75" in tags + assert "grind" in tags diff --git a/tests/test_problem_finder.py b/tests/test_problem_finder.py deleted file mode 100644 index fadf48b..0000000 --- a/tests/test_problem_finder.py +++ /dev/null @@ -1,27 +0,0 @@ -from leetcode_py.cli.utils.problem_finder import _build_problem_tags_cache, get_tags_for_problem - - -def test_build_problem_tags_cache_with_real_tags(): - result = _build_problem_tags_cache() - - # Test that grind tag includes both grind-75 problems and daily_temperatures - assert "daily_temperatures" in result - assert "grind" in result["daily_temperatures"] - - # Test that grind-75 problems also get grind tag - assert "two_sum" in result - assert "grind-75" in result["two_sum"] - assert "grind" in result["two_sum"] - assert "test" in result["two_sum"] - - -def test_get_tags_for_problem(): - # Test daily_temperatures has grind tag - tags = get_tags_for_problem("daily_temperatures") - assert "grind" in tags - - # Test grind-75 problem has both tags - tags = get_tags_for_problem("two_sum") - assert "grind-75" in tags - assert "grind" in tags - assert "test" in tags