diff --git a/929-Unique-Email-Addresses/note.md b/929-Unique-Email-Addresses/note.md new file mode 100644 index 0000000..10adda5 --- /dev/null +++ b/929-Unique-Email-Addresses/note.md @@ -0,0 +1,131 @@ +# 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 倍くらいしか早くなってない。 + +### 自分で書き直したコードのバリエーション + +```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/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..2d89c38 --- /dev/null +++ b/929-Unique-Email-Addresses/step2.py @@ -0,0 +1,11 @@ +class Solution: + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails = set() + for email in emails: + 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) 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)