-
Notifications
You must be signed in to change notification settings - Fork 0
929. Unique Email Addresses #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,238 @@ | ||||||
| // Step 1: | ||||||
| // 手作業でやる場合で考える。 | ||||||
| // | ||||||
| // 考慮すべき条件: | ||||||
| // - '@' を基準に、local name, domain name に分けられる。 | ||||||
| // - local name 内の '.' は無視する。 | ||||||
| // - local name 内に '+' がある場合は、'+' と '+' 以降の文字は全て無視される。 | ||||||
| // - domain は最後は '.com' で終わっている必要がある。 | ||||||
| // - domain は '.com' の前に最低一文字必要である。 | ||||||
| // | ||||||
| // 上記の条件を考慮した上で、複数のメールアドレスが与えられ、その中から重複のないメールアドレスの数を数え上げれば良い。 | ||||||
| // パッと思いつくのは、 | ||||||
| // 1. 上に書いた条件をもとに、email を変換 | ||||||
| // 2. 「既出のメール」をまとめる表みたいなものを用意して、それを見て既出かを確かめる。 | ||||||
| // 3. 既出ではなければ、unique email として判断。「既出のメール」をまとめる表に追加。1. に戻る。 | ||||||
| // 4. 既出であれば、何もせず、1. に戻る。 | ||||||
| // | ||||||
| // 変換の方法は、 | ||||||
| // 1. '@' を基準として、local, domain に分ける。 | ||||||
| // 2. local で、'+' を基準として、前半部分のみを取り出す。 | ||||||
| // 3. local の前半部分から、'.' を消去する。 | ||||||
| // 4. 処理を施した local + '@' + domain で最終的なメールを得る。 | ||||||
| // | ||||||
| // local の変換は、for 文で一文字目から順に走査して、'.' があればスキップ、'+'があればそこで終了。という形の方が one-way で終えられて早そう。 | ||||||
| // | ||||||
| // これをやれば、unique なメールの集合を取得できる。ただ今回求められているのは数字だけだから、もっと簡単にもできそうな気がしている。 | ||||||
| // | ||||||
| // 手作業から思いついたわけではないが、全てのメールアドレスを用いて Trie を作って、葉ノードの数を調べるのもありか。 | ||||||
| // メリットとしては、空間計算量があまり必要としないがありそうだが、通常 DB でメールアドレスを保存するのに Trie は向かなそうだからメリットにならなそう。 | ||||||
| // とりあえず、先ほどの手法を実装する。 | ||||||
|
|
||||||
| class Solution { | ||||||
| int numUniqueEmails(List<String> emails) { | ||||||
| var uniqueEmails = <String>{}; | ||||||
|
|
||||||
| for (var email in emails) { | ||||||
| if (!isValidEmail(email)) { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| var convertedEmail = convertEmail(email); | ||||||
| uniqueEmails.add(convertedEmail); | ||||||
| } | ||||||
|
|
||||||
| return uniqueEmails.length; | ||||||
| } | ||||||
|
|
||||||
| bool isValidEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| int atCount = parts.length - 1; | ||||||
| int maxAtCount = 1; | ||||||
| int minAtCount = 1; | ||||||
|
|
||||||
| if (!(minAtCount <= atCount && atCount <= maxAtCount)) { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| String domain = parts.last; | ||||||
| String domainTail = '.com'; | ||||||
|
|
||||||
| if (!domain.endsWith(domainTail)) { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| return domain != domainTail; | ||||||
| } | ||||||
|
|
||||||
| String convertEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| String originalLocal = parts.first; | ||||||
| String domain = parts.last; | ||||||
|
|
||||||
| StringBuffer buffer = StringBuffer(); | ||||||
|
|
||||||
| for (int i = 0; i < originalLocal.length; i++) { | ||||||
| String char = originalLocal[i]; | ||||||
|
|
||||||
| if (char == '+') { | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| if (char == '.') { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| buffer.write(char); | ||||||
| } | ||||||
|
|
||||||
| return buffer.toString() + '@' + domain; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Step 2: | ||||||
| // 他の人のコードを読む | ||||||
| // | ||||||
| // https://github.com/naoto-iwase/leetcode/pull/14/changes#diff-3282ee8d1849a45b92b2a4a6e00440ecc86e0ae920337bcef2ea279c2d29b848R74 | ||||||
| // split を一切使わないで、複数のフラグを立てて条件分岐することで email を変換する方法。書き方いろいろあるな。 | ||||||
| // | ||||||
| // https://github.com/Yuto729/LeetCode_arai60/pull/19/changes#r2609508628 | ||||||
| // parse_local_name という関数名に対して、 | ||||||
| // > 関数名から、どのような値が返ってくるのかが分かりにくく感じました。比較のために正規化する意味合いを込めて、 canonicalize() はいかがでしょうか? | ||||||
| // これは、自分の convertEmail にも当てはまる。命名をもう少しわかりやすくしていきたい。 | ||||||
| // | ||||||
| // https://github.com/plushn/SWE-Arai60/pull/14#discussion_r2051710985 | ||||||
| // ここの議論は面白いなーと思いました。string 周りの話はいつも面倒になって中途半端な理解で終わってしまうから、どこかで一気にやりたい。 | ||||||
| // | ||||||
| // https://github.com/hayashi-ay/leetcode/pull/25/changes#diff-d65d43698547a0f3cfcdb7f005de30ed4cd0c45ae015fd01094d6647cfa0a84aR202 | ||||||
| // 正規表現の手法。 | ||||||
| // 正規表現も書きたかったが、時間がないため一旦断念。 | ||||||
|
|
||||||
| class Solution { | ||||||
| int numUniqueEmails(List<String> emails) { | ||||||
| var uniqueEmails = <String>{}; | ||||||
|
|
||||||
| for (var email in emails) { | ||||||
| if (!isValidEmail(email)) { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| var canonicalizedEmail = canonicalizeEmail(email); | ||||||
| uniqueEmails.add(canonicalizedEmail); | ||||||
| } | ||||||
|
|
||||||
| return uniqueEmails.length; | ||||||
| } | ||||||
|
|
||||||
| bool isValidEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| int atCount = parts.length - 1; | ||||||
| int maxAtCount = 1; | ||||||
| int minAtCount = 1; | ||||||
|
|
||||||
| if (!(minAtCount <= atCount && atCount <= maxAtCount)) { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| String domain = parts.last; | ||||||
| String domainTail = '.com'; | ||||||
|
|
||||||
| if (!domain.endsWith(domainTail)) { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| return domain != domainTail; | ||||||
| } | ||||||
|
|
||||||
| String canonicalizeEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| String originalLocal = parts.first; | ||||||
| String domain = parts.last; | ||||||
|
|
||||||
| StringBuffer buffer = StringBuffer(); | ||||||
|
|
||||||
| for (int i = 0; i < originalLocal.length; i++) { | ||||||
| String char = originalLocal[i]; | ||||||
|
|
||||||
| if (char == '+') { | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| if (char == '.') { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| buffer.write(char); | ||||||
| } | ||||||
|
|
||||||
| return buffer.toString() + '@' + domain; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Step 3: | ||||||
| class Solution { | ||||||
| int numUniqueEmails(List<String> emails) { | ||||||
| var uniqueEmails = <String>{}; | ||||||
|
|
||||||
| for (var email in emails) { | ||||||
| if (!isValidEmail(email)) { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| final canonicalizedEmail = canonicalizeEmail(email); | ||||||
| uniqueEmails.add(canonicalizedEmail); | ||||||
| } | ||||||
|
|
||||||
| return uniqueEmails.length; | ||||||
| } | ||||||
|
|
||||||
| bool isValidEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| int atCount = parts.length - 1; | ||||||
| int maxAtCount = 1; | ||||||
| int minAtCount = 1; | ||||||
|
|
||||||
| if (!(minAtCount <= atCount && atCount <= maxAtCount)) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ご指摘のとおり拡張性を意識して定義していました。ただ、メールアドレスの特性上"@"が複数許容されることはほとんどなさそうなため、単に一つだけの方が良さそうです。 |
||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| String domain = parts.last; | ||||||
| String domainTail = '.com'; | ||||||
|
|
||||||
| if (!domain.endsWith(domainTail)) { | ||||||
| return false; | ||||||
| } | ||||||
|
|
||||||
| return domain != domainTail; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 個人的には、「
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かに伝わりづらいですね…。「domainTailの前に少なくとも1文字を含む」という条件を書き直すとすると例えば、 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
良さそうです! |
||||||
| } | ||||||
|
|
||||||
| String canonicalizeEmail(String email) { | ||||||
| final parts = email.split('@'); | ||||||
|
|
||||||
| String originalLocal = parts.first; | ||||||
| String domain = parts.last; | ||||||
|
|
||||||
| StringBuffer buffer = StringBuffer(); | ||||||
|
|
||||||
| for (int i = 0; i < originalLocal.length; i++) { | ||||||
| String char = originalLocal[i]; | ||||||
|
|
||||||
| if (char == '+') { | ||||||
| break; | ||||||
| } | ||||||
|
|
||||||
| if (char == '.') { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| buffer.write(char); | ||||||
| } | ||||||
|
Comment on lines
+222
to
+234
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dart未経験者として、 |
||||||
|
|
||||||
| return buffer.toString() + '@' + domain; | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[fyi]
本問の制約を超えていますが、local にクオートした
@を含むのは正当なようです。https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1