From eb45d1ae8bf35c2e6b6ddd58481797fa8fb9e728 Mon Sep 17 00:00:00 2001 From: mayota Date: Fri, 19 Jun 2026 21:59:14 +0900 Subject: [PATCH 1/3] step1,2 --- 929-Unique-Email-Addresses/note.md | 26 ++++++++++++++++++++++++++ 929-Unique-Email-Addresses/step1.py | 13 +++++++++++++ 929-Unique-Email-Addresses/step2.py | 21 +++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 929-Unique-Email-Addresses/note.md create mode 100644 929-Unique-Email-Addresses/step1.py create mode 100644 929-Unique-Email-Addresses/step2.py diff --git a/929-Unique-Email-Addresses/note.md b/929-Unique-Email-Addresses/note.md new file mode 100644 index 0000000..328e627 --- /dev/null +++ b/929-Unique-Email-Addresses/note.md @@ -0,0 +1,26 @@ +# 929. Unique Email Addresses + + + +## step1(まず通す) + +ひとまず、アドレスは制約を満たすようにバリデーションされている前提で考える。 + +パッと思いつくのは、 + +- まず `@` で local_name と domain_name に分ける +- local_name から、`+` で split して先頭だけをとってくる +- 残った部分から `.` を取り除く +- local_nameの残り@domain_name の形にして set に add + +「一つのアドレスを 3 回くらい線形になめてハッシュテーブルに挿入」を 100 ループくらいなので、 +配列の長さもアドレスの長さも 100 だとすると `(3 * 100 + set 化) * 100 ~ 数万-数十万` ステップくらい? python だと1秒あたり100万-1000万ステップくらいなので数十 ms くらい? + +実際 leetcode のジャッジで 57 ms くらいかかった。 + +## step2(整形&他の人のコードを読む) + +アドレス文字列を1度だけ先頭から見ていって処理すれば数倍は速いだろう。 +やや複雑だし、2-3 倍くらいしか早くなってない。 + +## step3(10分以内にさっとかける * 3回) diff --git a/929-Unique-Email-Addresses/step1.py b/929-Unique-Email-Addresses/step1.py new file mode 100644 index 0000000..70db4b8 --- /dev/null +++ b/929-Unique-Email-Addresses/step1.py @@ -0,0 +1,13 @@ +class Solution: + def numUniqueEmails(self, emails: list[str]) -> int: + unique_email = set() + for email in emails: + parts = email.split("@") + local_name = parts[0] + domain_name = parts[1] + + local_name = "".join(local_name.split("+")[0].split(".")) + + unique_email.add(local_name + "@" + domain_name) + + return len(unique_email) diff --git a/929-Unique-Email-Addresses/step2.py b/929-Unique-Email-Addresses/step2.py new file mode 100644 index 0000000..d8d8acf --- /dev/null +++ b/929-Unique-Email-Addresses/step2.py @@ -0,0 +1,21 @@ +class Solution: + def normalize(self, email: str) -> str: + local_name = "" + index = 0 + after_plus = False + while email[index] != "@": + if not after_plus: + if email[index] == "+": + after_plus = True + elif email[index] != ".": + local_name += email[index] + index += 1 + + return local_name + email[index:] + + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + normalized_email = self.normalize(email) + unique_emails.add(normalized_email) + return len(unique_emails) From 91f4ab65064d1a2078e818e7bccee9b2a3b37965 Mon Sep 17 00:00:00 2001 From: mayota Date: Fri, 19 Jun 2026 23:26:42 +0900 Subject: [PATCH 2/3] =?UTF-8?q?step2=20=E3=81=9D=E3=81=AE2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 929-Unique-Email-Addresses/note.md | 105 ++++++++++++++++++++++++++++ 929-Unique-Email-Addresses/step2.py | 22 ++---- 2 files changed, 111 insertions(+), 16 deletions(-) diff --git a/929-Unique-Email-Addresses/note.md b/929-Unique-Email-Addresses/note.md index 328e627..10adda5 100644 --- a/929-Unique-Email-Addresses/note.md +++ b/929-Unique-Email-Addresses/note.md @@ -23,4 +23,109 @@ アドレス文字列を1度だけ先頭から見ていって処理すれば数倍は速いだろう。 やや複雑だし、2-3 倍くらいしか早くなってない。 +### 自分で書き直したコードのバリエーション + +```python +class Solution: + def normalize(self, email: str) -> str: + local_name = "" + index = 0 + while index < len(email) and email[index] != "+": + if email[index] == "@": + return local_name + email[index:] + if email[index] != ".": + local_name += email[index] + index += 1 + + index = len(email) - 1 + while email[index] != "@": + index -= 1 + + return local_name + email[index:] + + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + normalized_email = self.normalize(email) + unique_emails.add(normalized_email) + return len(unique_emails) +``` + +```python +class Solution: + def normalize(self, email: str) -> str: + local_name = "" + index = 0 + while index < len(email) and email[index] != "@": + if email[index] == "+": + break + if email[index] != ".": + local_name += email[index] + index += 1 + + index = len(email) - 1 + while email[index] != "@": + index -= 1 + + return local_name + email[index:] + + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + normalized_email = self.normalize(email) + unique_emails.add(normalized_email) + return len(unique_emails) +``` + +```python +class Solution: + def normalize(self, email: str) -> str: + local_name = "" + index = 0 + after_plus = False + while email[index] != "@": + if not after_plus: + if email[index] == "+": + after_plus = True + elif email[index] != ".": + local_name += email[index] + index += 1 + + return local_name + email[index:] + + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + normalized_email = self.normalize(email) + unique_emails.add(normalized_email) + return len(unique_emails) +``` + +### 他の人のコードを読む + +- + - replace の存在を忘れていた + - この練習会で久しぶりにコードを書いているので結構組み込み型のメソッドとかを忘れている + - 何かを調べるのではなくて全体を読む感じでドキュメントを眺めるのも有効かもしれない + - インライン関数をよく見かける。 vs. クラスの別メソッドとして書くのトレードオフを理解したい + - 正規表現は直感に沿うやり方 + - split で maxsplit 1 を指定する代わりに partition っていうメソッドもあるらしい + - 不正な入力を弾くのは実用上大事そう + - email address では何が「正しい」? + - 思ったより使える文字種が多いしルールもまあまあ複雑なのでちゃんと書くと大変そう + - RFC: など + - とはいえルールが明確なので地道に書いていけば良さそう + - RFC違反のメールアドレスについて日本年金機構やドコモが注意喚起をしている + - + - + - 違反してなくても `a!"\""@example.com` みたいなの(多分違反してない)をちゃんと処理できないソフトウェアもそこそこありそう? + - だいたいライブラリを使うんだろうけど + - この関数でやるよりは事前にやってて欲しい気がする +- + - ord (文字コード)には気をつけないといけない + - a-z が連続しているとは限らない + +問題の constraints だけだと +`.@example.com` が入力として許容されるのでおかしそう? + ## step3(10分以内にさっとかける * 3回) diff --git a/929-Unique-Email-Addresses/step2.py b/929-Unique-Email-Addresses/step2.py index d8d8acf..2d89c38 100644 --- a/929-Unique-Email-Addresses/step2.py +++ b/929-Unique-Email-Addresses/step2.py @@ -1,21 +1,11 @@ class Solution: - def normalize(self, email: str) -> str: - local_name = "" - index = 0 - after_plus = False - while email[index] != "@": - if not after_plus: - if email[index] == "+": - after_plus = True - elif email[index] != ".": - local_name += email[index] - index += 1 - - return local_name + email[index:] - def numUniqueEmails(self, emails: list[str]) -> int: unique_emails = set() for email in emails: - normalized_email = self.normalize(email) - unique_emails.add(normalized_email) + at_split = email.split("@") + domain_name = at_split[1] + local_name = at_split[0] + normalized_local_name = local_name.split("@")[0].replace(".", "") + unique_emails.add(normalized_local_name + "@" + domain_name) + return len(unique_emails) From f51656cfc8d9d9b33e1aa3b9a278441e0b6471c9 Mon Sep 17 00:00:00 2001 From: mayota Date: Fri, 19 Jun 2026 23:39:58 +0900 Subject: [PATCH 3/3] step3 --- 929-Unique-Email-Addresses/step3.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 929-Unique-Email-Addresses/step3.py diff --git a/929-Unique-Email-Addresses/step3.py b/929-Unique-Email-Addresses/step3.py new file mode 100644 index 0000000..45e1629 --- /dev/null +++ b/929-Unique-Email-Addresses/step3.py @@ -0,0 +1,11 @@ +class Solution: + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + email_parts = email.partition("@") + domain_name = email_parts[2] + local_name = email_parts[0] + normalized_local_name = local_name.split("+")[0].replace(".", "") + unique_emails.add(f"{normalized_local_name}@{domain_name}") + + return len(unique_emails)