题目分析

本题模拟了基于 SMTP(Simple Mail Transfer Protocol)\texttt{SMTP(Simple Mail Transfer Protocol)}SMTPSimple Mail Transfer Protocol 的电子邮件传输过程。题目要求根据给定的 MTA(Message Transfer Agent)\texttt{MTA(Message Transfer Agent)}MTAMessage Transfer Agent 信息和一系列邮件消息,模拟发送方 MTA 与接收方 MTA\texttt{MTA}MTA 之间的通信过程,并按照指定格式输出通信记录。

关键点

  • 每个 MTA\texttt{MTA}MTA 包含一个名称和一个用户集合。
  • 每封邮件有一个发送者和若干个接收者(格式为 用户@MTA名称)。
  • 对于每个接收方 MTA\texttt{MTA}MTA,需要建立一次 SMTP\texttt{SMTP}SMTP 会话。
  • 在同一个 MTA\texttt{MTA}MTA 内的多个接收者,在一次会话中处理。
  • 如果接收方 MTA\texttt{MTA}MTA 中没有某个用户,则返回响应码 550,但这不影响其他有效用户的投递。
  • 如果某个 MTA\texttt{MTA}MTA 中没有有效接收者,则不会发送 DATA 命令。
  • 通信顺序与输入文件中接收方 MTA\texttt{MTA}MTA 出现的顺序一致。
  • 输出格式要求:每段会话以 Connection between X and Y 开头,每行命令或响应前有 444 个空格,且不应有多余的空格。

解题思路

  1. 数据结构设计

    • 使用结构体 MTA 存储 MTA\texttt{MTA}MTA 名称和用户集合(set<string>)。
    • 使用 vector<MTA> 存储所有 MTA\texttt{MTA}MTA
    • 使用 map<string, int> 建立 MTA\texttt{MTA}MTA 名称到索引的映射,便于快速查找。
  2. 输入处理

    • 读取 MTA\texttt{MTA}MTA 信息直到遇到单独一行的 *
    • 读取邮件消息直到遇到单独一行的 *(表示结束)。
    • 每封邮件的输入格式为:
      • 第一行:发送者和所有接收者(空格分隔)。
      • 随后一行为 *(消息开始标志)。
      • 接下来多行为邮件正文,直到下一个 *(消息结束标志)。
  3. 邮件处理流程

    • 解析发送者的用户名和 MTA\texttt{MTA}MTA 名称。
    • 将所有接收者按目标 MTA\texttt{MTA}MTA 分组,同时去重(同一 MTA\texttt{MTA}MTA 内同一用户只出现一次)。
    • 按接收方 MTA\texttt{MTA}MTA 在输入中出现的顺序,逐个处理 SMTP\texttt{SMTP}SMTP 会话。
    • 对于每个接收方 MTA\texttt{MTA}MTA
      • 输出连接信息。
      • 依次发送 HELOMAIL FROM 命令。
      • 对每个接收者发送 RCPT TO 命令,根据用户是否存在返回 250550
      • 如果有至少一个有效接收者,则发送 DATA 命令,接着发送邮件正文,以单独一行的 . 结束。
      • 发送 QUIT 命令结束会话。
  4. 注意事项

    • 无效用户不影响其他有效用户的投递。
    • 若一个 MTA\texttt{MTA}MTA中没有有效接收者,则不发送 DATA 部分。
    • 输出格式必须严格符合要求,包括缩进和空行。

代码实现

// The Letter Carrier's Rounds
// UVa ID: 814
// Verdict: Accepted
// Submission Date: 2025-10-19
// UVa Run Time: 0.030s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <sstream>

using namespace std;

// 存储MTA信息的结构
struct MTA {
    string name;
    set<string> users;
};

// 解析用户@MTA格式
void parseAddress(const string& address, string& user, string& mta) {
    size_t atPos = address.find('@');
    user = address.substr(0, atPos);
    mta = address.substr(atPos + 1);
}

int main() {
    vector<MTA> mtas;
    string line;
    
    // 读取MTA信息
    while (getline(cin, line) && line != "*") {
        if (line[0] == 'M' && line[1] == 'T' && line[2] == 'A') {
            istringstream iss(line);
            string mta, name;
            int userCount;
            iss >> mta >> name >> userCount;
            
            MTA newMTA;
            newMTA.name = name;
            
            for (int i = 0; i < userCount; i++) {
                string user;
                iss >> user;
                newMTA.users.insert(user);
            }
            
            mtas.push_back(newMTA);
        }
    }
    
    // 创建MTA名称到索引的映射,便于查找
    map<string, int> mtaMap;
    for (int i = 0; i < mtas.size(); i++) {
        mtaMap[mtas[i].name] = i;
    }

    // 处理消息
    while (getline(cin, line) && line != "*") {
        // 解析发送者和接收者(都在同一行)
        istringstream iss(line);
        string sender;
        iss >> sender;
        
        string senderUser, senderMTA;
        parseAddress(sender, senderUser, senderMTA);
        
        // 读取所有接收者(同一行的剩余部分)
        vector<string> allRecipients;
        string recipient;
        while (iss >> recipient) {
            allRecipients.push_back(recipient);
        }
        
        // 忽略消息的起始标志*
        getline(cin, line);
    
        // 读取消息内容(直到下一个*)
        vector<string> messageLines;
        while (getline(cin, line) && line != "*") {
            messageLines.push_back(line);
        }
        
        // 按目标MTA分组接收者
        map<string, vector<string>> mtaRecipients;
        vector<string> lineMtas;
        set<string> mtasCache;
        map<string, set<string>> userCache;
        for (const auto& recipient : allRecipients) {
            string user, mta;
            parseAddress(recipient, user, mta);
            if (!userCache[mta].count(user)) {
                mtaRecipients[mta].push_back(user);
                userCache[mta].insert(user);
            }
            if (!mtasCache.count(mta)) {
                mtasCache.insert(mta);
                lineMtas.push_back(mta);
            }
        }

        for (const string mta : lineMtas) {
            const string& targetMTA = mta;
            const vector<string>& users = mtaRecipients[mta];
            
            // 检查目标MTA是否存在
            if (mtaMap.find(targetMTA) == mtaMap.end()) {
                continue;
            }
            
            const MTA& destMTA = mtas[mtaMap[targetMTA]];
            
            // 输出连接信息
            cout << "Connection between " << senderMTA << " and " << targetMTA << endl;
            
            // HELO命令
            cout << "     HELO " << senderMTA << endl;
            cout << "     250" << endl;
            
            // MAIL FROM命令
            cout << "     MAIL FROM:<" << senderUser << "@" << senderMTA << ">" << endl;
            cout << "     250" << endl;
            
            // 检查有效接收者
            bool hasValidRecipient = false;
            for (const string& user : users) {
                cout << "     RCPT TO:<" << user << "@" << targetMTA << ">" << endl;
                if (destMTA.users.find(user) != destMTA.users.end()) {
                    cout << "     250" << endl;
                    hasValidRecipient = true;
                } else {
                    cout << "     550" << endl;
                }
            }
            
            // 如果有有效接收者,发送DATA
            if (hasValidRecipient) {
                cout << "     DATA" << endl;
                cout << "     354" << endl;
                
                // 输出消息内容
                for (const string& msgLine : messageLines) {
                    cout << "     " << msgLine << endl;
                }
                
                // 结束消息
                cout << "     ." << endl;
                cout << "     250" << endl;
            }
            
            // QUIT命令
            cout << "     QUIT" << endl;
            cout << "     221" << endl;
        }
    }
    
    return 0;
}

总结

本题是一道模拟题,重点在于理解 SMTP\texttt{SMTP}SMTP 协议的基本流程,并按照题目要求准确地实现通信逻辑和输出格式。需要注意的细节包括:

  • 接收者去重。
  • MTA\texttt{MTA}MTA 分组处理。
  • 无效用户不影响有效用户。
  • 输出格式的缩进和空行。

通过合理的数据结构和清晰的流程控制,可以高效地解决本题。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐