<|instruction|>
Solve the below problem. The solution should start with an abridged problem statement. Then key observations. Then full solution based on the observations. Then C++ and Python implementations with comments.

274. Spam-filter
time limit per test: 0.25 sec.
memory limit per test: 65536 KB
input: standard
output: standard



After tremendous last-year success not yet well-known, but already grown-up company H&H decided to create a new software masterpiece -- a world-leading spam-filter.
Due to Your poor last year -- a world-leading spam-filter. Due to your poor last year project performance, you are given a small and boring task again.
Your task is to write a simple email-validator.

Valid email address can be described as follows:
<letter> ::= a|b|...|z|A|B|...|Z

<symbol> ::= <letter>|0|1|...|9|_|-

<word> ::= <symbol>|<symbol><word>

<prefix> ::= <word>|<prefix>.<word>

<domain> ::= <letter><letter>|<letter><letter><letter>

<suffix> ::= <prefix>.<domain>

<address> ::= <prefix>@<suffix>

Input
The first line of the input file contains integer number N (1 <= N <= 100) -- the number of email addresses to be checked. Each of the following N lines contains one email address. Email address is the non-empty sequence of characters with ASCII codes from 32 to 255 no more then 100 characters long.

Output
For each address from the input write a separate line with the word YES, if the email address is valid and NO if it is not.

Sample test(s)

Input
3
abc@abc
abc@abc.abc
_@-.ru

Output
NO
YES
YES
Author:	Ilya V. Elterman
Resource:	ACM ICPC 2004-2005, NEERC, Southern Subregional Contest
Date:	Saratov, October 7, 2004

<|response|>
## 1. Abridged Problem Statement

Given N candidate email addresses (1 <= N <= 100, each up to 100 characters long), determine for each whether it matches the following grammar:

  address ::= prefix '@' suffix
  prefix  ::= word ('.' word)*
  word    ::= one or more symbols
  symbol  ::= letter | digit | '_' | '-'
  suffix  ::= prefix '.' domain
  domain  ::= exactly 2 or 3 letters

Report "YES" if the address is valid, otherwise "NO".

## 2. Key Observations

- There must be an '@' splitting the string into a left part (local prefix) and a right part (suffix). A second '@' cannot appear in a valid address because '@' is not a valid prefix symbol, so it is rejected by the prefix check anyway.
- A prefix (on either side of '@' or before the final dot in the suffix)
  - is non-empty
  - does not begin or end with '.'
  - does not contain consecutive dots ("..")
  - contains only symbols or dots, where a symbol is letter, digit, '_' or '-'.
- The suffix must contain at least one dot. Splitting at the last dot gives:
  - a sub-prefix (validated by the same prefix rules)
  - a domain of length 2 or 3 made only of letters.
- All checks can be done in a single linear pass per string (O(length_of_string)).

## 3. Full Solution Approach

For each email string S:

  1. Find the '@'. If there is none, reject.
  2. Split into local = S[0..at-1], rest = S[at+1..end].
  3. Validate local with prefix rules. If invalid, reject.
  4. In rest, find the last dot. If none, reject.
  5. Split rest into mid = rest[0..dot-1], dom = rest[dot+1..end].
  6. Validate mid with prefix rules. If invalid, reject.
  7. Check dom length is 2 or 3 and all characters are letters. If not, reject.
  8. If all checks pass, accept; otherwise reject.

## 4. C++ Implementation

```cpp
#include <bits/stdc++.h>

using namespace std;

template<typename T1, typename T2>
ostream& operator<<(ostream& out, const pair<T1, T2>& x) {
    return out << x.first << ' ' << x.second;
}

template<typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& x) {
    return in >> x.first >> x.second;
}

template<typename T>
istream& operator>>(istream& in, vector<T>& a) {
    for(auto& x: a) {
        in >> x;
    }
    return in;
};

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& a) {
    for(auto x: a) {
        out << x << ' ';
    }
    return out;
};

int n;
vector<string> emails;

bool is_symbol(char ch) {
    return isalpha(ch) || isdigit(ch) || ch == '_' || ch == '-';
}

bool is_valid_prefix(const string& prefix) {
    if(prefix.empty() || prefix.front() == '.' || prefix.back() == '.') {
        return false;
    }
    bool last_char_was_dot = false;
    for(char ch: prefix) {
        if(ch == '.') {
            if(last_char_was_dot) {
                return false;
            }
            last_char_was_dot = true;
        } else if(!is_symbol(ch)) {
            return false;
        } else {
            last_char_was_dot = false;
        }
    }
    return true;
}

bool is_valid_domain(const string& domain) {
    if(domain.size() != 2 && domain.size() != 3) {
        return false;
    }
    for(char ch: domain) {
        if(!isalpha(ch)) {
            return false;
        }
    }
    return true;
}

bool is_valid_suffix(const string& suffix) {
    size_t pos = suffix.rfind('.');
    if(pos == string::npos) {
        return false;
    }
    string prefix = suffix.substr(0, pos);
    string domain = suffix.substr(pos + 1);
    return is_valid_prefix(prefix) && is_valid_domain(domain);
}

bool is_valid_email(const string& email) {
    size_t pos = email.find('@');
    if(pos == string::npos) {
        return false;
    }
    string prefix = email.substr(0, pos);
    string suffix = email.substr(pos + 1);
    return is_valid_prefix(prefix) && is_valid_suffix(suffix);
}

void read() {
    cin >> n;
    cin.ignore();
    emails.resize(n);
    for(auto& email: emails) {
        getline(cin, email);
    }
}

void solve() {
    // Validate each email against the grammar by splitting on punctuation:
    //
    // - An address is <prefix>@<suffix>. The prefix is a dot-separated list of
    //   non-empty words over [A-Za-z0-9_-], so it may not start or end with a
    //   dot and may not contain two consecutive dots.
    //
    // - The suffix is <prefix>.<domain>: split at the last dot, the part before
    //   must be a valid prefix and the domain must be 2 or 3 letters.
    //
    // - For each line print YES if it parses and NO otherwise.

    vector<string> results;
    for(const auto& email: emails) {
        results.push_back(is_valid_email(email) ? "YES" : "NO");
    }
    for(const auto& result: results) {
        cout << result << '\n';
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    // cin >> T;
    for(int test = 1; test <= T; test++) {
        read();
        // cout << "Case #" << test << ": ";
        solve();
    }

    return 0;
}
```

## 5. Python Implementation with Detailed Comments

```python
import sys

def is_symbol(ch):
    # Letters, digits, underscore or hyphen
    return ch.isalpha() or ch.isdigit() or ch in '_-'

def is_valid_prefix(pref):
    # Must be non-empty
    if not pref:
        return False
    # Cannot start or end with dot
    if pref[0] == '.' or pref[-1] == '.':
        return False
    last_dot = False
    for ch in pref:
        if ch == '.':
            # No consecutive dots
            if last_dot:
                return False
            last_dot = True
        else:
            # Every non-dot must be a symbol
            if not is_symbol(ch):
                return False
            last_dot = False
    return True

def is_valid_domain(dom):
    # Exactly 2 or 3 letters
    return len(dom) in (2, 3) and all(ch.isalpha() for ch in dom)

def is_valid_suffix(suf):
    # Must contain at least one dot
    i = suf.rfind('.')
    if i == -1:
        return False
    mid, dom = suf[:i], suf[i+1:]
    return is_valid_prefix(mid) and is_valid_domain(dom)

def is_valid_email(email):
    # Must contain a '@'
    if '@' not in email:
        return False
    i = email.find('@')
    local, rest = email[:i], email[i+1:]
    return is_valid_prefix(local) and is_valid_suffix(rest)

def main():
    data = sys.stdin.read().splitlines()
    n = int(data[0])
    for email in data[1:1+n]:
        print("YES" if is_valid_email(email) else "NO")

if __name__ == "__main__":
    main()
```

Explanation of Steps in Code:
- We first split at the '@' to isolate the local-part and the suffix.
- `is_valid_prefix` enforces the word-and-dot rules for both the local part and the prefix of the suffix; it also rejects any stray second '@' since that is not a valid symbol.
- `is_valid_suffix` splits at the last dot to extract the domain, then validates both sub-prefix and domain.
- The domain is checked for exact length (2 or 3) and letters only.
- If every check passes, we output "YES"; otherwise, "NO".
