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 '+'
.
"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.
"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.
"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 '@'
.emails[i]
contains exactly one '@'
character.'+'
character.".com"
suffix.".com"
suffix.This problem involves processing a list of email addresses and determining the number of unique addresses after applying specific filtering rules. The rules are:
Periods in local name: Periods (.
) in the local name part of the email are ignored. alice.z@leetcode.com
becomes alicez@leetcode.com
.
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:
Initialization: Create a HashSet
(or equivalent) called uniqueEmails
to store the unique email addresses.
Iteration: Iterate through each email address in the input array emails
.
Splitting: Split each email address into local and domain parts using the "@" symbol as a delimiter.
Local Name Processing: Process the local name part:
processedLocal
..
), skip it.+
), break the loop (ignore the rest of the local name).processedLocal
.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.
Return the Count: Finally, return the size of the uniqueEmails
set, which represents the number of unique email addresses.
Time Complexity Analysis:
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:
uniqueEmails
set stores unique email addresses. In the worst case, all email addresses are unique, resulting in a space complexity of O(N).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.
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)
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();
}
}
/**
* @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;
};
#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.