1 条题解

  • 0
    @ 2025-5-4 12:55:27

    解题思路

    1. 问题分析
      根据原子表计算分子式的总重量。分子式可能包含嵌套括号和数字乘数,需正确处理这些结构。若存在未知原子或格式错误,输出 UNKNOWN

    2. 方法选择
      使用 递归下降法 解析分子式,递归处理嵌套括号结构。递归方法自然处理括号内的子表达式,逐层计算总重量,并验证原子是否存在。

    3. 步骤分解

      • 读取原子表:存入哈希表以便快速查找。
      • 解析分子式
        • 递归处理括号:遇到 ( 递归解析子表达式,处理完后读取乘数。
        • 解析原子:读取原子符号(可能含小写字母),验证存在性。
        • 处理数字乘数:括号后乘数必须为 2-99;原子后乘数若存在需为 2-99,否则默认为 1。
      • 验证合法性:检查原子存在性、括号匹配性及乘数合法性。

    代码实现

    #include <iostream>
    #include <string>
    #include <map>  // 使用std::map替代std::unordered_map
    #include <cctype>  // 用于字符类型判断函数(isdigit/isupper等)
    
    using namespace std;
    
    // 解析结果结构体,用于递归传递解析状态
    struct ParseResult {
        int total;    // 当前解析部分的总原子重量
        int pos;      // 当前解析到的字符串位置
        bool valid;   // 解析是否合法(原子存在性、格式正确性)
        ParseResult() : total(0), pos(0), valid(true) {}  // 默认初始化
    };
    
    /**
     * 递归解析分子式
     * @param s 输入的分子式字符串
     * @param pos 当前解析的起始位置
     * @param atom_map 原子重量哈希表
     * @return ParseResult 包含总重量、最终位置、合法性的结构体
     */
    ParseResult parse(const string& s, int pos, const map<string, int>& atom_map) {
        ParseResult res;
        res.pos = pos;  // 初始化起始位置
    
        // 循环解析直到遇到右括号或字符串结束
        while (res.pos < s.size() && s[res.pos] != ')' && res.valid) {
            // 情况1:遇到左括号,递归处理子表达式
            if (s[res.pos] == '(') {  
                res.pos++;  // 跳过左括号
                ParseResult sub = parse(s, res.pos, atom_map);  // 递归解析括号内内容
                
                // 检查子表达式是否合法且以右括号闭合
                if (!sub.valid || sub.pos >= s.size() || s[sub.pos] != ')') {
                    res.valid = false;
                    break;
                }
                sub.pos++;  // 跳过右括号
                
                // 读取括号后的乘数(必须为2-99)
                int num = 0;
                int start_num = sub.pos;  // 记录数字起始位置
                while (sub.pos < s.size() && isdigit(s[sub.pos])) {
                    num = num * 10 + (s[sub.pos] - '0');
                    sub.pos++;
                }
                // 乘数合法性检查(必须有数字且范围正确)
                if (start_num == sub.pos || num < 2 || num > 99) {
                    res.valid = false;
                    break;
                }
                
                res.total += sub.total * num;  // 累加子表达式总重量(乘以乘数)
                res.pos = sub.pos;  // 更新当前解析位置
    
            // 情况2:处理原子符号
            } else {  
                // 原子符号必须以大写字母开头
                if (!isupper(s[res.pos])) {
                    res.valid = false;
                    break;
                }
                
                // 读取原子符号(可能包含后续小写字母)
                string atom(1, s[res.pos]);  // 初始化为首字母
                res.pos++;
                if (res.pos < s.size() && islower(s[res.pos])) {  // 检查是否有小写字母
                    atom += s[res.pos];
                    res.pos++;
                }
                
                // 检查原子是否存在原子表中
                map<string, int>::const_iterator it = atom_map.find(atom);
                if (it == atom_map.end()) {
                    res.valid = false;
                    break;
                }
                
                // 获取原子重量并处理后续数字乘数
                int weight = it->second;
                int num = 0;
                int start_num = res.pos;
                while (res.pos < s.size() && isdigit(s[res.pos])) {  // 读取数字
                    num = num * 10 + (s[res.pos] - '0');
                    res.pos++;
                }
                
                // 处理乘数规则:
                // 1. 无数字时默认乘数为1
                // 2. 有数字时必须为2-99
                if (start_num != res.pos) {  
                    if (num < 2 || num > 99) {
                        res.valid = false;
                        break;
                    }
                } else {  
                    num = 1;
                }
                
                res.total += weight * num;  // 累加当前原子总重量
            }
        }
        return res;
    }
    
    int main() {
        map<string, int> atom_map;  // 原子符号 -> 原子重量
        
        // 阶段1:读取原子表
        string line;
        while (getline(cin, line)) {
            if (line == "END_OF_FIRST_PART") break;
            
            string atom;
            int weight = 0;
            bool read_atom = true;  // 标记当前是否在读取原子符号
            
            // 分割原子符号和重量(按空格/tab分隔)
            for (size_t i = 0; i < line.size(); ++i) {
                char c = line[i];
                if (c == ' ' || c == '\t') {
                    read_atom = false;  // 遇到分隔符,开始读取重量
                } else if (read_atom) {
                    atom.push_back(c);  // 拼接原子符号
                } else if (isdigit(c)) {  
                    weight = weight * 10 + (c - '0');  // 计算重量数值
                }
            }
            
            // 存入哈希表(确保非空原子和合法重量)
            if (!atom.empty() && weight > 0) {
                atom_map[atom] = weight;
            }
        }
    
        // 阶段2:处理分子式
        while (getline(cin, line)) {
            if (line == "0") break;
            
            ParseResult result = parse(line, 0, atom_map);  // 解析分子式
            
            // 输出条件:完全解析且无格式错误
            if (result.valid && result.pos == line.size()) {
                cout << result.total << endl;
            } else {
                cout << "UNKNOWN" << endl;  // 存在未知原子或格式错误
            }
        }
    
        return 0;
    }
    

    注释说明

    结构体 ParseResult 保存解析过程中的总重量 (total)、当前解析位置 (pos) 和合法性状态 (valid),用于递归传递结果。

    递归函数 parse

    括号处理:遇到 ( 递归解析子表达式,计算子表达式总重量后读取乘数并应用。

    原子处理:读取原子符号并验证存在性,处理显式或隐式乘数。

    数字验证:括号后乘数必须为 2-99,原子后乘数若存在则需在 2-99 之间。

    主函数逻辑

    读取原子表:逐行解析原子符号和重量,存入哈希表。

    解析分子式:对每个分子式调用 parse,检查结果合法性并输出。

    关键细节

    原子符号识别:正确处理大写+小写字母组合(如 He)。

    错误处理:在解析过程中实时检查格式错误,确保及时终止非法解析。

    递归控制:通过 pos 精确控制解析进度,避免越界或死循环。

    • 1

    信息

    ID
    1046
    时间
    1000ms
    内存
    30MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者