1 条题解

  • 0
    @ 2025-11-28 14:29:18

    你好!我是你的OI教练。

    这道题(CSP-J 2021 T3 网络连接)是一道大模拟题目。这类题目的特点是逻辑分支多、细节要求严,但不需要高深的算法。核心难点在于字符串的合法性判定

    下面我为你提供解题思路和代码提示:

    1. 解题思路分解

    我们需要处理 NN 条指令,每条指令包含类型(Server/Client)和地址串。

    步骤一:判断地址串是否合法(最核心)

    一个合法的地址串 a.b.c.d:e 必须满足非常严格的条件。手动解析字符串容易漏掉边界情况(比如多余的前导0、数字越界、格式错误等)。

    这里有一个非常巧妙的技巧:“解析-重构-对比”法

    1. 解析:使用 sscanf 按照 %lld.%lld.%lld.%lld:%lld 的格式尝试读取 5 个数字。
    2. 范围检查:检查读到的数字是否在题目要求的范围内(0a,b,c,d2550 \le a,b,c,d \le 255, 0e655350 \le e \le 65535)。
    3. 重构:将读取到的数字用 sprintf 重新按照标准格式 %lld.%lld.%lld.%lld:%lld 输出到一个新的字符串缓冲区。
    4. 对比:比较“重构后的字符串”与“原始输入字符串”是否完全一致。
      • 这个步骤能解决所有棘手问题:
      • 如果有前导0(如 01),sscanf 会读成 1sprintf 写回 1,与原串 01 不相等 \to 判错。
      • 如果有非法字符(如 1.1.1.1:80abc),sscanf 读完数字后停止,sprintf 只有数字部分,与原串不相等 \to 判错。
      • 如果格式不对(如 1.1.1.1.80),sscanf 无法读满 5 个数 \to 判错。

    步骤二:逻辑处理

    使用一个 哈希表(Map) 来存储已经成功建立连接的服务机。

    • map<string, int> mp;
    • Key: 地址串(string)
    • Value: 服务机的编号(int)

    对于第 ii 条指令:

    1. 检查合法性:如果不合法,输出 ERR
    2. 如果是 Server
      • 检查 Map 中是否已存在该地址。
      • 存在 \to 输出 FAIL
      • 不存在 \to 输出 OK,并将 {地址, i} 存入 Map。
    3. 如果是 Client
      • 检查 Map 中是否已存在该地址。
      • 存在 \to 输出 Map 中对应的编号。
      • 不存在 \to 输出 FAIL

    2. 标准 AC 代码

    #include <iostream>
    #include <string>
    #include <map>
    #include <cstdio> // 用于 sscanf 和 sprintf
    
    using namespace std;
    
    // 核心函数:检查字符串 s 是否符合规范
    bool check(string s) {
        long long a, b, c, d, e;
        
        // 1. 尝试按照特定格式提取 5 个数字
        // s.c_str() 将 C++ string 转为 C 风格字符串
        int count = sscanf(s.c_str(), "%lld.%lld.%lld.%lld:%lld", &a, &b, &c, &d, &e);
        
        // 必须成功读取到 5 个数字
        if (count != 5) return false;
        
        // 2. 检查数字范围
        if (a < 0 || a > 255) return false;
        if (b < 0 || b > 255) return false;
        if (c < 0 || c > 255) return false;
        if (d < 0 || d > 255) return false;
        if (e < 0 || e > 65535) return false;
        
        // 3. "解析-重构-对比" 验证法
        // 将提取出的数字重新按标准格式印回字符串
        char buf[100];
        sprintf(buf, "%lld.%lld.%lld.%lld:%lld", a, b, c, d, e);
        
        // 如果原字符串是 "192.168.0.1:080" (含前导0)
        // sscanf 读出 e=80
        // sprintf 写回 buf="192.168.0.1:80"
        // 此时 buf != s,说明不规范
        string res = buf;
        return res == s;
    }
    
    int main() {
        int n;
        cin >> n;
        
        // 映射表:存储已成功的 Server 地址 -> 编号
        map<string, int> servers;
        
        for (int i = 1; i <= n; i++) {
            string op, ad;
            cin >> op >> ad;
            
            // 1. 先判断合法性
            if (!check(ad)) {
                cout << "ERR" << endl;
                continue;
            }
            
            // 2. 根据类型处理
            if (op == "Server") {
                if (servers.count(ad)) {
                    // 地址已存在
                    cout << "FAIL" << endl;
                } else {
                    // 成功建立
                    servers[ad] = i;
                    cout << "OK" << endl;
                }
            } else { // Client
                if (servers.count(ad)) {
                    // 找到对应的服务机,输出编号
                    cout << servers[ad] << endl;
                } else {
                    // 未找到
                    cout << "FAIL" << endl;
                }
            }
        }
        
        return 0;
    }
    

    3. 注意事项

    1. 数据类型:解析 IP 地址的数字时,建议使用 long long。虽然合法的数字不超过 65535,但输入可能会给出一个超级大的数字(如 1.1.1.1:99999999999),使用 int 可能会溢出,而 sscanf 配合 long long 能正确读取并进行后续的范围判断。
    2. sscanf 返回值:它返回成功匹配并赋值的参数个数。
    3. Map 的复杂度map 的查询和插入复杂度是 O(logN×L)O(\log N \times L),其中 LL 是字符串长度。本题 N=1000N=1000,非常快,完全不会超时。

    希望这个思路能帮你顺利解决这道大模拟题!加油!

    • 1

    信息

    ID
    12044
    时间
    1000ms
    内存
    32MiB
    难度
    5
    标签
    递交数
    1
    已通过
    1
    上传者