From 7261a5446230c9664564d3dc8c274098cbd26370 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 6 Apr 2026 09:25:37 +0800 Subject: [PATCH] test: migrate LeetCode 929 to new directory structure with tests and docs - Migrate Unique Email Addresses from legacy "Hash Table/" directory to new structure - Add comprehensive complexity analysis in solution docstring (O(n*m) time, O(n*m) space) - Add pytest test coverage with LeetCode examples - Update README.md statistics: 87 problems (61 Easy, 25 Medium, 1 Hard) - Add problem entry to docs/EASY.md --- Hash Table/929. Unique Email Addresses.py | 16 ------------ README.md | 4 +-- docs/EASY.md | 3 ++- .../unique_email_addresses_929/__init__.py | 0 .../unique_email_addresses_929/solution.py | 26 +++++++++++++++++++ .../test_solution.py | 16 ++++++++++++ 6 files changed, 46 insertions(+), 19 deletions(-) delete mode 100644 Hash Table/929. Unique Email Addresses.py create mode 100644 problems/easy/unique_email_addresses_929/__init__.py create mode 100644 problems/easy/unique_email_addresses_929/solution.py create mode 100644 problems/easy/unique_email_addresses_929/test_solution.py diff --git a/Hash Table/929. Unique Email Addresses.py b/Hash Table/929. Unique Email Addresses.py deleted file mode 100644 index 4c66364..0000000 --- a/Hash Table/929. Unique Email Addresses.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import List - - -class Solution: - def numUniqueEmails(self, emails: List[str]) -> int: - res = 0 - emails_received = set() - for email in emails: - parts = email.split('@') - parts[0] = parts[0].replace('.', '') - parts[0] = parts[0].split('+')[0] - email_res = parts[0] + '@' + parts[1] - if email_res not in emails_received: - emails_received.add(email_res) - res += 1 - return res diff --git a/README.md b/README.md index aa6df2d..75421c4 100644 --- a/README.md +++ b/README.md @@ -41,11 +41,11 @@ Each problem follows this structure: ## Solutions -📊 **Statistics**: 86 problems solved (migrated to new structure) +📊 **Statistics**: 87 problems solved (migrated to new structure) | Difficulty | Count | |------------|-------| -| Easy | 60 | +| Easy | 61 | | Medium | 25 | | Hard | 1 | diff --git a/docs/EASY.md b/docs/EASY.md index c355f51..954794e 100644 --- a/docs/EASY.md +++ b/docs/EASY.md @@ -1,6 +1,6 @@ # Easy Problems -Total: 60 problems solved +Total: 61 problems solved ## Solutions @@ -35,6 +35,7 @@ Total: 60 problems solved | 709 | [To Lower Case](https://leetcode.com/problems/to-lower-case/) | [solution.py](../problems/easy/to_lower_case_709/solution.py) | [test_solution.py](../problems/easy/to_lower_case_709/test_solution.py) | | 771 | [Jewels and Stones](https://leetcode.com/problems/jewels-and-stones/) | [solution.py](../problems/easy/jewels_and_stones_771/solution.py) | [test_solution.py](../problems/easy/jewels_and_stones_771/test_solution.py) | | 804 | [Unique Morse Code Words](https://leetcode.com/problems/unique-morse-code-words/) | [solution.py](../problems/easy/unique_morse_code_words_804/solution.py) | [test_solution.py](../problems/easy/unique_morse_code_words_804/test_solution.py) | +| 929 | [Unique Email Addresses](https://leetcode.com/problems/unique-email-addresses/) | [solution.py](../problems/easy/unique_email_addresses_929/solution.py) | [test_solution.py](../problems/easy/unique_email_addresses_929/test_solution.py) | | 977 | [Squares of a Sorted Array](https://leetcode.com/problems/squares-of-a-sorted-array/) | [solution.py](../problems/easy/squares_of_a_sorted_array_977/solution.py) | [test_solution.py](../problems/easy/squares_of_a_sorted_array_977/test_solution.py) | | 1119 | [Remove Vowels from a String](https://leetcode.com/problems/remove-vowels-from-a-string/) | [solution.py](../problems/easy/remove_vowels_from_a_string_1119/solution.py) | [test_solution.py](../problems/easy/remove_vowels_from_a_string_1119/test_solution.py) | | 1160 | [Find Words That Can Be Formed by Characters](https://leetcode.com/problems/find-words-that-can-be-formed-by-characters/) | [solution.py](../problems/easy/find_words_that_can_be_formed_by_characters_1160/solution.py) | [test_solution.py](../problems/easy/find_words_that_can_be_formed_by_characters_1160/test_solution.py) | diff --git a/problems/easy/unique_email_addresses_929/__init__.py b/problems/easy/unique_email_addresses_929/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/problems/easy/unique_email_addresses_929/solution.py b/problems/easy/unique_email_addresses_929/solution.py new file mode 100644 index 0000000..2c8f25d --- /dev/null +++ b/problems/easy/unique_email_addresses_929/solution.py @@ -0,0 +1,26 @@ +# Tags: Array, String, Hash Set +from typing import List + + +class Solution: + def num_unique_emails(self, emails: List[str]) -> int: + """ + Complexity Analysis: + Time: O(n * m) + - n: number of emails + - m: average length of each email + - We iterate through each email once, and for each email we perform + split, replace operations which are O(m) + + Space: O(n * m) + - In the worst case, all emails are unique after normalization + - Each normalized email could be up to O(m) in length + - The set stores up to n unique emails + """ + unique_emails = set() + for email in emails: + names = email.split('@') + local_name = names[0].split('+')[0].replace('.', '') + domain_name = names[1] + unique_emails.add(local_name + '@' + domain_name) + return len(unique_emails) diff --git a/problems/easy/unique_email_addresses_929/test_solution.py b/problems/easy/unique_email_addresses_929/test_solution.py new file mode 100644 index 0000000..5aefb2f --- /dev/null +++ b/problems/easy/unique_email_addresses_929/test_solution.py @@ -0,0 +1,16 @@ +import pytest +from tests.base_test import BaseTestSolution +from .solution import Solution + + +class TestSolution(BaseTestSolution): + solution = Solution() + + @pytest.mark.parametrize("method_name, emails, expected, timeout", [ + ('num_unique_emails', + ["test.email+alex@leetcode.com", "test.e.mail+bob.cathy@leetcode.com", "testemail+david@lee.tcode.com"], 2, + None), + ('num_unique_emails', ["a@leetcode.com", "b@leetcode.com", "c@leetcode.com"], 3, None), + ]) + def test_num_unique_emails(self, method_name, emails, expected, timeout): + self._run_test(self.solution, method_name, (emails,), expected, timeout)