LeetCode 726.原子的数量

给你一个字符串化学式 formula ,返回 每种原子的数量 。

原子总是以一个大写字母开始,接着跟随 0 个或任意个小写字母,表示原子的名字。

如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。

例如,"H2O" 和 "H2O2" 是可行的,但 "H1O2" 这个表达是不可行的。

两个化学式连在一起可以构成新的化学式。

例如 "H2O2He3Mg4" 也是化学式。

由括号括起的化学式并佐以数字(可选择性添加)也是化学式。

例如 "(H2O2)" 和 "(H2O2)3" 是化学式。

返回所有原子的数量,格式为:第一个(按字典序)原子的名字,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。

示例 1:

输入:formula = "H2O"

输出:"H2O"

解释:原子的数量是 {'H': 2, 'O': 1}。

示例 2:

输入:formula = "Mg(OH)2"

输出:"H2MgO2"

解释:原子的数量是 {'H': 2, 'Mg': 1, 'O': 2}。

示例 3:

输入:formula = "K4(ON(SO3)2)2"

输出:"K4N2O14S4"

解释:原子的数量是 {'K': 4, 'N': 2, 'O': 14, 'S': 4}。

提示:

1 <= formula.length <= 1000

formula 由英文字母、数字、'(' 和 ')' 组成

formula 总是有效的化学式

递归处理,遇到括号压栈,括号结束出栈,并计算括号内的原子数:

cpp 复制代码
class Solution {
public:
    string countOfAtoms(string formula) {
        stack<map<string, int>> stk;
        
        map<string, int> curCnt;
        string curName = "";
        int curNum = 0;
        for (int i = 0; i < formula.size(); ++i) {
            char c = formula[i];

            // 遇到新的元素,或遇到括号时,说明处理完一个元素了
            if (isupper(c) || c == '(' || c == ')') {
                // 将得到的元素和数量计入答案
                if (!curName.empty()) {
                    curNum = max(1, curNum);

                    curCnt[curName] += curNum;
                }

                curName = "";
                curNum = 0;
            }

            // 遇到左括号,入栈当前层的原子及其数量
            if (c == '(') {
                stk.push(curCnt);
                curCnt.clear();
            }

            // 遇到右括号
            if (c == ')') {
                // 看括号内的集合会重复几次
                ++i;
                int num = 0;
                while (i < formula.size() && isdigit(formula[i])) {
                    num = num * 10 + (formula[i] - '0');
                    ++i;
                }
                num = max(num, 1);

                // 括号内的原子数量重复num次
                for (auto &p : curCnt) {
                    p.second *= num;
                }

                // 上一层的原子数量也加到结果里
                for (auto &p : stk.top()) {
                    curCnt[p.first] += p.second * num;
                }

                stk.pop();

                --i;
            }

            if (isalpha(c)) {
                curName.push_back(c);
            } else if (isdigit(c)) {
                curNum = curNum * 10 + (c - '0');
            }

            if (i == formula.size() - 1) {
                if (!curName.empty()) {
                    curNum = max(1, curNum);

                    curCnt[curName] += curNum;
                }
            }
        }

        string res;
        for (auto p : curCnt) {
            res += p.first;
            if (p.second != 1) {
                res += to_string(p.second);
            }
        }

        return res;
    }
};

如果formula的长度为n,则此算法时间复杂度为O(n2^22),首先遍历formula的时间是n,最差情况下,即尽可能多层嵌套时,如(B(C(D)n1)n2)n3,此时curCnt最多只会有一个原子,出栈时也只会有1个原子,但出栈阶段curCnt会线性增长到O(n)级别,每次出栈都会遍历curCnt,让curCnt的每个原子重复括号后的数字次,因此内层循环也需要O(n)时间;空间复杂度为O(n),因为stk的最大深度是O(n)级别。

相关推荐
.道阻且长.5 小时前
2.LeetCode算法习题讲解--双指针--复写零
算法·leetcode·职场和发展
ltl6 小时前
内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战
linux
·薯条大王6 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
To_OC7 小时前
LC 438 找到所有字母异位词:暴力超时后,我靠滑动窗口一招搞定
javascript·算法·leetcode
小小、码农9 小时前
Linux进程控制:从fork到exec,手写一个简易Shell
linux·运维·服务器
不会就选b9 小时前
Linux之进程间通信(二)---模拟进程池
linux·运维·服务器
Forever Nore10 小时前
学完C语言力扣第一题做不来正常吗
数据结构·算法
hansang_IR11 小时前
【题解】LC:倍增 / 区间并查集(Range Parallel Unionfind)
c++·算法·并查集
沫璃染墨11 小时前
《从零入门Linux系统篇(十五):系统工具篇·六——GDB调试器详解:从程序执行控制到高级调试技巧》
linux·运维·服务器·c语言·c++