Leetcode 929. Unique Email Addresses

The problem requires us to find the number of unique email addresses. These email addresses are subject to two rules. The first rule specifies that if a period (.) is added in the local part of the email address, the email sent will be forwarded to the address without the periods. The second rule is that if a plus (+) sign is added in the local part, everything after the plus sign will be ignored.

To solve this problem, we'll use a hash set to keep track of all the unique addresses. We iterate through the list of email addresses, process the local part according to the two rules defined above, and then add the processed local part concatenated with the domain to the set. The size of the set at the end will be the number of unique email addresses.

Let's explain this with an example. Suppose we have the following list of emails: ["[email protected]","[email protected]","[email protected]"]. Here's how we'll process each email.

  1. "[email protected]" -> "[email protected]"
  2. "[email protected]" -> "[email protected]"
  3. "[email protected]" -> "[email protected]"

The result is 2 because the addresses "[email protected]" and "[email protected]" are unique.

Python solution

1
2python
3class Solution:
4    def numUniqueEmails(self, emails):
5        normalized = set()
6        
7        for email in emails:
8            local, domain = email.split('@')
9            local = local.split('+')[0].replace('.', '')
10            normalized.add(local + '@' + domain)
11        
12        return len(normalized)

Java solution

1
2java
3import java.util.HashSet;
4
5class Solution {
6    public int numUniqueEmails(String[] emails) {
7        HashSet<String> normalized = new HashSet<>();
8        for (String email : emails) {
9            int splitPoint = email.indexOf('@');
10            String local = email.substring(0, splitPoint);
11            String domain = email.substring(splitPoint);
12            if (local.contains("+")) {
13                local = local.substring(0, local.indexOf('+'));
14            }
15            // Note: replaceAll is used to remove all occurrences of '.'
16            local = local.replaceAll("\\.", "");
17            normalized.add(local + domain);
18        }
19        return normalized.size();
20    }
21}

JavaScript solution

1
2javascript
3class Solution {
4    numUniqueEmails(emails) {
5        let normalized = [...new Set(emails)];
6        
7        return normalized.map(mail=>{
8          let [local, domain] = mail.split('@');
9          local = local.split("+")[0].replace(/\./g, "");
10          return local + "@" + domain;
11        }).length;
12    }
13}
14

C++ solution

1
2c++
3class Solution {
4public:
5    int numUniqueEmails(vector<string>& emails) {
6        unordered_set<string> normalized;
7        for (string& email : emails) {
8            string local = email.substr(0, email.find('@'));
9            string domain = email.substr(email.find('@'));
10            local = local.substr(0, local.find('+'));
11            local.erase(remove(local.begin(), local.end(), '.'), local.end());
12            normalized.insert(local + domain);
13        }
14        return normalized.size();
15    }
16};

C# solution

1
2csharp
3public class Solution {
4    public int NumUniqueEmails(string[] emails) {
5        var normalized = new HashSet<string>();
6        foreach (var email in emails) {
7            var split = email.Split('@');
8            var local = split[0].Split('+')[0].Replace(".", "");
9            normalized.Add(local + "@" + split[1]);
10        }
11        return normalized.Count;
12    }
13}

In all the solutions, we first split the local part and the domain part using '@'. Then we process the local part according to the rules. After processing, we concatenate them and insert into the set. The size of the set gives us the number of unique email addresses.In the C# solution, the 'Split' function is used to divide the email string into two parts: the local and the domain. The '+' sign is used as a delimiter to further split the local part up to the point where the '+' sign appears, and then the "." character is removed using the 'Replace' method. Once the local part has been processed according to the rules, it is joined back with the domain part and added to the 'normalized' HashSet.

In contrast, the Java solution makes use of the 'substring' method to divide the email string. The local part undergoes similar processing, but here the 'replaceAll' method is used to remove the '.' character. The processed email is then added to the 'normalized' HashSet.

Moving to Python, the 'split' function performs the string division. The plus sign and periods are handled similarly as in the C# solution.

The JavaScript solution is similar to the Python one in terms of string manipulation, with the key difference being the use of the 'replace' method via regular expressions to replace periods with an empty string in the local part.

Lastly, the C++ solution introduces another way of achieving string manipulation, primarily the 'find' and 'erase' functions. These help to index the '@' position, extract the local and domain parts, locate the '+' sign, and remove the dot characters before storing the processed email in the 'normalized' unordered_set.

All these solutions basically follow the same general algorithmic steps but vary on how string manipulation is handled based on the language-specific methods and functions. The time and space complexity of all these solutions is O(N) where N is the length of the input list. This is due to the fact that all the given emails are processed once and kept in a data structure (HashSet or Set or unordered_set), which supports average constant time insertions and retrievals.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫