Skip to content

3860. Unique Email Groups πŸ”’

Description

You are given an array of strings emails, where each string is a valid email address.

Two email addresses belong to the same group if both their normalized local names and normalized domain names are identical.

The normalization rules are as follows:

  • The local name is the part before the '@' symbol.
    • Ignore all dots '.'.
    • Ignore everything after the first '+', if present.
    • Convert to lowercase.
  • The domain name is the part after the '@' symbol.
    • Convert to lowercase.

Return an integer denoting the number of unique email groups after normalization.

Β 

Example 1:

Input: emails = ["test.email+alex@leetcode.com", "test.e.mail+bob.cathy@leetcode.com", "testemail+david@lee.tcode.com"]

Output: 2

Explanation:

Email Local Normalized Local Domain Normalized Domain Final Email
test.email+alex@leetcode.com test.email+alex testemail leetcode.com leetcode.com testemail@leetcode.com
test.e.mail+bob.cathy@leetcode.com test.e.mail+bob.cathy testemail leetcode.com leetcode.com testemail@leetcode.com
testemail+david@lee.tcode.com testemail+david testemail lee.tcode.com lee.tcode.com testemail@lee.tcode.com

Unique emails are ["testemail@leetcode.com", "testemail@lee.tcode.com"]. Thus, the answer is 2.

Example 2:

Input: emails = ["A@B.com", "a@b.com", "ab+xy@b.com", "a.b@b.com"]

Output: 2

Explanation:

Email Local Normalized Local Domain Normalized Domain Final Email
A@B.com A a B.com b.com a@b.com
a@b.com a a b.com b.com a@b.com
ab+xy@b.com ab+xy ab b.com b.com ab@b.com
a.b@b.com a.b ab b.com b.com ab@b.com

Unique emails are ["a@b.com", "ab@b.com"]. Thus, the answer is 2.

Example 3:

Input: emails = ["a.b+c.d+e@DoMain.com", "ab+xyz@domain.com", "ab@domain.com"]

Output: 1

Explanation:

Email Local Normalized Local Domain Normalized Domain Final Email
a.b+c.d+e@DoMain.com a.b+c.d+e ab DoMain.com domain.com ab@domain.com
ab+xyz@domain.com ab+xyz ab domain.com domain.com ab@domain.com
ab@domain.com ab ab domain.com domain.com ab@domain.com

All emails normalize to "ab@domain.com". Thus, the answer is 1.

Β 

Constraints:

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

Solutions

Solution 1: Hash Table

We can use a hash set \(\textit{st}\) to store the normalized result of each email address. For each email address, we normalize it according to the problem requirements:

  • Split the email address into a local name and a domain name.
  • For the local name, remove all dots ., and if a plus sign + exists, remove the plus sign and everything after it. Then convert the local name to lowercase.
  • For the domain name, convert it to lowercase.
  • Concatenate the normalized local name and domain name to obtain the normalized email address, and add it to the hash set \(\textit{st}\).

Finally, the number of elements in the hash set \(\textit{st}\) is the number of unique email groups.

The time complexity is \(O(n \cdot m)\), where \(n\) and \(m\) are the number of email addresses and the average length of each email address, respectively. The space complexity is \(O(n \cdot m)\), in the worst case where all email addresses are distinct.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def uniqueEmailGroups(self, emails: list[str]) -> int:
        st = set()
        for email in emails:
            local, domain = email.split("@")
            local = local.split("+")[0].replace(".", "").lower()
            domain = domain.lower()
            normalized = local + domain
            st.add(normalized)
        return len(st)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
    public int uniqueEmailGroups(String[] emails) {
        Set<String> st = new HashSet<>();

        for (String email : emails) {
            String[] parts = email.split("@");
            String local = parts[0];
            String domain = parts[1];

            int plusIndex = local.indexOf('+');
            if (plusIndex != -1) {
                local = local.substring(0, plusIndex);
            }

            local = local.replace(".", "").toLowerCase();
            domain = domain.toLowerCase();

            String normalized = local + domain;
            st.add(normalized);
        }

        return st.size();
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
    int uniqueEmailGroups(vector<string>& emails) {
        unordered_set<string> st;

        for (auto& email : emails) {
            int atPos = email.find('@');
            string local = email.substr(0, atPos);
            string domain = email.substr(atPos + 1);

            int plusPos = local.find('+');
            if (plusPos != string::npos) {
                local = local.substr(0, plusPos);
            }

            string cleaned;
            for (char c : local) {
                if (c != '.') {
                    cleaned += tolower(c);
                }
            }

            for (char& c : domain) {
                c = tolower(c);
            }

            st.insert(cleaned + domain);
        }

        return st.size();
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func uniqueEmailGroups(emails []string) int {
    st := make(map[string]struct{})

    for _, email := range emails {
        parts := strings.Split(email, "@")
        local := parts[0]
        domain := parts[1]

        if idx := strings.Index(local, "+"); idx != -1 {
            local = local[:idx]
        }

        local = strings.ReplaceAll(local, ".", "")
        local = strings.ToLower(local)
        domain = strings.ToLower(domain)

        normalized := local + domain
        st[normalized] = struct{}{}
    }

    return len(st)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function uniqueEmailGroups(emails: string[]): number {
    const st = new Set<string>();

    for (const email of emails) {
        let [local, domain] = email.split('@');
        local = local.split('+')[0].replace(/\./g, '').toLowerCase();
        domain = domain.toLowerCase();

        const normalized = local + domain;
        st.add(normalized);
    }

    return st.size;
}

Comments