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.

Note: This problem is from LeetCode.
❌ Compiled
❌ Executed
❌ Correct
program main
    implicit none

    integer :: i, j, n
    character(len=100) :: emails(100), local_name, domain_name
    logical :: has_plus, has_dot

    ! read input
    read(*,*) n
    do i = 1, n
        read(*,*) emails(i)
    end do

    ! count unique addresses
    j = 0
    do i = 1, n
        ! extract local name and domain name
        local_name = trim(emails(i))
        domain_name = ''
        has_plus = .false.
        has_dot = .false.
        do while (local_name /= '')
            if (local_name(1:1) == '+') then
                has_plus = .true.
                exit
            else if (local_name(1:1) == '.') then
                has_dot = .true.
            end if
            local_name = local_name(2:)
        end do
        if (.not. has_plus) then
            do while (domain_name /= '')
                if (domain_name(1:1) == '.') then
                    domain_name = domain_name(2:)
                else
                    exit
                end if
            end do
        end if
        if (.not. has_dot) then
            domain_name = trim(emails(i))
        end if

        ! check if address is unique
        if (.not. any(trim(local_name) == trim(emails(:i-1)))) then
            j = j + 1
        end if
    end do

    ! print output
    write(*,*) j

end program main
❌ Compiled
❌ Executed
❌ Correct
!include(sort_strings.f90)

program main
    implicit none

    integer, parameter :: STDIN = 5, STDOUT = 6
    character(len=100) :: line
    integer :: i, j, n
    character(len=100), allocatable :: emails(:)

    ! Read the number of emails
    read(STDIN, *) n

    ! Allocate memory for the emails
    allocate(emails(n))

    ! Read the emails
    do i = 1, n
        read(STDIN, *) emails(i)
    end do

    ! Sort the emails
    call sort_strings(emails, n)

    ! Print the unique emails
    do i = 1, n
        write(STDOUT, *) emails(i)
    end do

    ! Deallocate the memory
    deallocate(emails)
end program main

!------------------------------------------------------------------------------
! Sort_strings.f90
!------------------------------------------------------------------------------

subroutine sort_strings(strings, n)
    implicit none

    integer, intent(in) :: n
    character(len=*), intent(inout) :: strings(n)

    integer :: i, j
    character(len=:), allocatable :: temp

    do i = 1, n - 1
        do j = i + 1, n
            if (strings(i) > strings(j)) then
                temp = strings(i)
                strings(i) = strings(j)
                strings(j) = temp
            end if
        end do
    end do
end subroutine sort_strings

!------------------------------------------------------------------------------
! Tests
!------------------------------------------------------------------------------

program test
    use sort_strings
    implicit none

    integer, parameter :: STDIN = 5, STDOUT = 6
    character(len=100) :: line
    integer :: i, j, n
    character(len=100), allocatable :: emails(:)

    ! Test case 1
    n = 3
    allocate(emails(n))
    emails = ["test.email+alex@leetcode.com ", "test.e.mail+bob.cathy@leetcode.com ", "testemail+david@lee.tcode.com "]
    call sort_strings(emails, n)
    write(STDOUT, *) emails
    deallocate(emails)

    ! Test case 2
    n = 3
    allocate(emails(n))
    emails = ["a@leetcode.com ", "b@leetcode.com ", "c@leetcode.com "]
    call sort_strings(emails, n)
    write(STDOUT, *) emails
    deallocate(emails)
end program test
🌐 Data from online sources
def num_special_equivalent_groups(words):
    groups = set()

    for word in words:
        evens = "".join(sorted(word[0::2]))
        odds = "".join(sorted(word[1::2]))

        groups.add(evens + odds)

    return len(groups)
1. Initialize an empty set to store the unique groups.
  1. For each word in the input list: a. Initialize two empty strings evens and odds to store even and odd indexed characters. b. Iterate through the word, and for each character, append it to either evens or odds based on its index being even or odd. c. Sort the evens and odds strings. d. Concatenate evens and odds, and insert the result into the set. If this string is already in the set, it doesn't get duplicated.
  2. Return the size of the set, which represents the number of unique groups.
🌐 Data from online sources
#include <vector>
#include <string>
#include <unordered_set>
#include <algorithm>

int numSpecialEquivalentGroups(std::vector<std::string>& words) {
    std::unordered_set<std::string> groups;

    for (std::string word : words) {
        std::string evens, odds;

        for (size_t i = 0; i < word.size(); ++i) {
            if (i % 2 == 0) evens += word[i];
            else odds += word[i];
        }

        std::sort(evens.begin(), evens.end());
        std::sort(odds.begin(), odds.end());

        groups.insert(evens + odds);
    }

    return groups.size();
}
1. Initialize an empty set to store the unique groups.
  1. For each word in the input list: a. Initialize two empty strings evens and odds to store even and odd indexed characters. b. Iterate through the word, and for each character, append it to either evens or odds based on its index being even or odd. c. Sort the evens and odds strings. d. Concatenate evens and odds, and insert the result into the set. If this string is already in the set, it doesn't get duplicated.
  2. Return the size of the set, which represents the number of unique groups.