1 条题解
-
0
解题思路
-
问题分析
根据原子表计算分子式的总重量。分子式可能包含嵌套括号和数字乘数,需正确处理这些结构。若存在未知原子或格式错误,输出UNKNOWN
。 -
方法选择
使用 递归下降法 解析分子式,递归处理嵌套括号结构。递归方法自然处理括号内的子表达式,逐层计算总重量,并验证原子是否存在。 -
步骤分解
- 读取原子表:存入哈希表以便快速查找。
- 解析分子式:
- 递归处理括号:遇到
(
递归解析子表达式,处理完后读取乘数。 - 解析原子:读取原子符号(可能含小写字母),验证存在性。
- 处理数字乘数:括号后乘数必须为 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
- 上传者