{x}
blog image

Unique Email Addresses

Every valid email consists of a local name and a domain name, separated by the '@' sign. Besides lowercase letters, the email may contain one or more '.' or '+'.

  • For example, in "alice@leetcode.com", "alice" is the local name, and "leetcode.com" is the domain name.

If you add periods '.' between some characters in the local name part of an email address, mail sent there will be forwarded to the same address without dots in the local name. Note that this rule does not apply to domain names.

  • For example, "alice.z@leetcode.com" and "alicez@leetcode.com" forward to the same email address.

If you add a plus '+' in the local name, everything after the first plus sign will be ignored. This allows certain emails to be filtered. Note that this rule does not apply to domain names.

  • For example, "m.y+name@email.com" will be forwarded to "my@email.com".

It is possible to use both of these rules at the same time.

Given an array of strings emails where we send one email to each emails[i], return the number of different addresses that actually receive mails.

 

Example 1:

Input: emails = ["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com","testemail+david@lee.tcode.com"]
Output: 2
Explanation: "testemail@leetcode.com" and "testemail@lee.tcode.com" actually receive mails.

Example 2:

Input: emails = ["a@leetcode.com","b@leetcode.com","c@leetcode.com"]
Output: 3

 

Constraints:

  • 1 <= emails.length <= 100
  • 1 <= emails[i].length <= 100
  • emails[i] consist of lowercase English letters, '+', '.' and '@'.
  • Each emails[i] contains exactly one '@' character.
  • All local and domain names are non-empty.
  • Local names do not start with a '+' character.
  • Domain names end with the ".com" suffix.
  • Domain names must contain at least one character before ".com" suffix.

Solution Explanation: Unique Email Addresses

This problem involves processing a list of email addresses and determining the number of unique addresses after applying specific filtering rules. The rules are:

  1. Periods in local name: Periods (.) in the local name part of the email are ignored. alice.z@leetcode.com becomes alicez@leetcode.com.

  2. Plus signs in local name: Everything after the first plus sign (+) in the local name is ignored. m.y+name@email.com becomes my@email.com.

The most efficient approach uses a HashSet (or Set in other languages) to store unique email addresses. This data structure inherently handles duplicate removal.

Algorithm:

  1. Initialization: Create a HashSet (or equivalent) called uniqueEmails to store the unique email addresses.

  2. Iteration: Iterate through each email address in the input array emails.

  3. Splitting: Split each email address into local and domain parts using the "@" symbol as a delimiter.

  4. Local Name Processing: Process the local name part:

    • Create a temporary string processedLocal.
    • Iterate through the characters of the local name.
    • If a character is a period (.), skip it.
    • If a character is a plus sign (+), break the loop (ignore the rest of the local name).
    • Otherwise, append the character to processedLocal.
  5. Combining and Adding to Set: Concatenate processedLocal and the domain part (with "@" in between) to form a processed email address. Add this processed email address to the uniqueEmails set. The set automatically handles duplicates.

  6. Return the Count: Finally, return the size of the uniqueEmails set, which represents the number of unique email addresses.

Time Complexity Analysis:

  • The algorithm iterates through each email address once (O(N), where N is the number of emails).
  • Processing each local name takes time proportional to its length. In the worst case, the length of the local names across all emails is O(L), where L is the total length of all characters in all email local names.

Therefore, the overall time complexity is O(N + L). In practice, L is often comparable to N, so it can be simplified to O(N) or O(L) depending on the relative sizes of N and L.

Space Complexity Analysis:

  • The uniqueEmails set stores unique email addresses. In the worst case, all email addresses are unique, resulting in a space complexity of O(N).
  • The temporary variables used during processing have constant space complexity, O(1).

Therefore, the overall space complexity is O(N).

Code Examples (Multiple Languages):

The code examples below demonstrate the algorithm in several programming languages. They all follow the same fundamental steps described above.

Python

class Solution:
    def numUniqueEmails(self, emails: List[str]) -> int:
        unique_emails = set()
        for email in emails:
            local, domain = email.split('@')
            processed_local = ""
            for char in local:
                if char == '.':
                    continue
                elif char == '+':
                    break
                else:
                    processed_local += char
            unique_emails.add(processed_local + "@" + domain)
        return len(unique_emails)

Java

import java.util.HashSet;
import java.util.Set;
 
class Solution {
    public int numUniqueEmails(String[] emails) {
        Set<String> uniqueEmails = new HashSet<>();
        for (String email : emails) {
            String[] parts = email.split("@");
            String local = parts[0];
            String domain = parts[1];
            StringBuilder processedLocal = new StringBuilder();
            for (char c : local.toCharArray()) {
                if (c == '.') continue;
                if (c == '+') break;
                processedLocal.append(c);
            }
            uniqueEmails.add(processedLocal.toString() + "@" + domain);
        }
        return uniqueEmails.size();
    }
}

JavaScript

/**
 * @param {string[]} emails
 * @return {number}
 */
var numUniqueEmails = function(emails) {
    const uniqueEmails = new Set();
    for (const email of emails) {
        const [local, domain] = email.split('@');
        let processedLocal = "";
        for (let i = 0; i < local.length; i++) {
            const char = local[i];
            if (char === '.') continue;
            if (char === '+') break;
            processedLocal += char;
        }
        uniqueEmails.add(processedLocal + "@" + domain);
    }
    return uniqueEmails.size;
};

C++

#include <iostream>
#include <string>
#include <vector>
#include <set>
 
using namespace std;
 
class Solution {
public:
    int numUniqueEmails(vector<string>& emails) {
        set<string> uniqueEmails;
        for (const string& email : emails) {
            size_t atPos = email.find('@');
            string local = email.substr(0, atPos);
            string domain = email.substr(atPos + 1);
            string processedLocal = "";
            for (char c : local) {
                if (c == '.') continue;
                if (c == '+') break;
                processedLocal += c;
            }
            uniqueEmails.insert(processedLocal + "@" + domain);
        }
        return uniqueEmails.size();
    }
};

These examples provide a clear and concise implementation of the algorithm in different programming languages, illustrating how the core logic remains consistent while adapting to language-specific syntax and data structures. Remember that the use of a Set or HashSet is crucial for efficient duplicate removal.