[Hdfs] lc726. 原子的数量(模拟+dfs+栈+字符串处理+好题)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Hdfs] lc726. 原子的数量(模拟+dfs+栈+字符串处理+好题)相关的知识,希望对你有一定的参考价值。

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;
    }
};

以上是关于[Hdfs] lc726. 原子的数量(模拟+dfs+栈+字符串处理+好题)的主要内容,如果未能解决你的问题,请参考以下文章

[JavaScript 刷题] 栈 - 原子的数量, leetcode 726

[JavaScript 刷题] 栈 - 原子的数量, leetcode 726

LeetCode 726 原子的数量[Map 栈] HERODING的LeetCode之路

LeetCode 451. 根据字符出现频率排序 / 645. 错误的集合 / 726. 原子的数量 / NC52 括号序列 / NC102 最近公共祖先 / NC78 反转链表

使用 LC3 模拟器计算二进制数中 1 的数量

[M贪心] lc678. 有效的括号字符串(括号问题+思维+模拟+dp)