1. 题目来源
链接:726. 原子的数量
进阶题:第十八次CCF计算机软件能力认证:3284. 化学方程式
栈题解:wzc大佬写的栈题解
2. 题目解析
题意十分明确,像是一个模拟、栈的问题。但是要写出精巧的代码很困难。
每个化学元素以大写字母开头,后跟小写字母,系数组成。
嵌套括号的话,每个括号内部应该单独处理。一般用栈维护左括号、右括号匹配情况。也可用 dfs
来处理。
本题要求按照字典序输出,那么每个括号内部的元素用 map<string, int>
来维护。则思路如下:
- 顺序遍历字符串,出现三种情况:
- 若当前遍历字符为左括号,则递归处理括号内部元素,返回值为
map<string, int> t
,里面存的就是按字典序排列的原子和原子数量。- 注意处理括号外面的系数,并分配给括号里面的每个原子,这里是系数成立原子个数的关系。
- 注意,本层的
map<string, int> res
是代表(xxx(xxx)x)
最外层这个括号的,而返回值t
返回的是内层括号的所有嵌套值,括号外层的系数针对的是外层这个括号,所以应当遍历t
中的所有原子及个数,+=
到res
中!。
- 若当前遍历字符为右括号,则说明完成了一个括号内部原子的判断,则本层递归返回。因为存在
(H2O2)
这样的样例,不是每个括号后面都是有系数的… - 都不是的话,只能是普通原子了,则将大写字母+小写字母+数字扣出来,组成原子添加到本层的
map<string, int>
中。
- 若当前遍历字符为左括号,则递归处理括号内部元素,返回值为
经典的模拟题,配合代码注释来学习其中的巧妙之处吧!
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
dfs
class Solution {
public:
typedef map<string, int> MPSI;
MPSI dfs(string &str, int &u) {
MPSI res;
while (u < str.size()) {
if (str[u] == '(') { // 遇见左括号,括号内部递归处理
u ++ ; // 跳过左括号
auto t = dfs(str, u); // 括号内部递归处理,得到括号里面的元素
u ++ ; // 跳过右括号
int cnt = 1, k = u;
while (k < str.size() && isdigit(str[k])) k ++ ; // 查看是否括号后跟系数
if (k > u) { // 有系数
cnt = stoi(str.substr(u, k - u));
u = k;
}
// res 是外层的所有元素,其中包含着 t 中的所有元素
// 将 t 中括号内部的元素乘以它自己括号外的倍数
// t 的整体包含在 res 中,递归也是为了展开这个内部括号 t 中的元素
// 在这 debug 很久...
for (auto &[x, y] : t) res[x] += y * cnt;
} else if (str[u] == ')') break;
else {
// 处理一般情况,单个化学式
int k = u + 1; // 跳过大写字母
while (k < str.size() && str[k] >= 'a' && str[k] <= 'z') k ++ ;
auto s = str.substr(u, k - u); // 得到化学式
u = k; // u 向后走
int cnt = 1; // 求该化学式系数
while (k < str.size() && isdigit(str[k])) k ++ ;
if (k > u) {
cnt = stoi(str.substr(u, k - u));
u = k;
}
res[s] += cnt; // 单个化学式,次数 +=
}
}
return res;
}
string countOfAtoms(string formula) {
int u = 0;
MPSI t = dfs(formula, u);
string res;
for (auto& [k, v] : t) {
res += k;
if (v > 1) res += to_string(v);
}
return res;
}
};
栈题解:wzc大佬写的栈题解
class Solution {
public:
int get_num(const string &s, int &i) {
int n = s.length();
if (i == n || !(s[i] >= '0' && s[i] <= '9'))
return 1;
int cnt = 0;
while (i < n && s[i] >= '0' && s[i] <= '9') {
cnt = cnt * 10 + s[i] - '0';
i++;
}
return cnt;
}
string get_token(const string &s, int &i) {
int n = s.length();
string token;
token += s[i++];
while (i < n && s[i] >= 'a' && s[i] <= 'z') {
token += s[i];
i++;
}
return token;
}
string countOfAtoms(string formula) {
stack<pair<string, int>> st;
int n = formula.length();
int i = 0;
while (i < n) {
if (formula[i] == '(') {
i++;
st.push(make_pair("(", -1));
} else if (formula[i] == ')') {
i++;
int cnt = get_num(formula, i);
vector<pair<string, int>> tmp;
while (st.top().first != "(") {
tmp.emplace_back(st.top().first, st.top().second * cnt);
st.pop();
}
st.pop();
for (const auto &t : tmp)
st.push(t);
} else {
string name(get_token(formula, i));
int cnt = get_num(formula, i);
st.push(make_pair(name, cnt));
}
}
unordered_map<string, int> h;
while (!st.empty()) {
h[st.top().first] += st.top().second;
st.pop();
}
vector<pair<string, int>> res(h.begin(), h.end());
sort(res.begin(), res.end());
string ans;
for (const auto &at : res) {
ans += at.first;
if (at.second > 1)
ans += to_string(at.second);
}
return ans;
}
};