[LeetCode] Number of Atoms 原子的個數


 

Given a chemical formula (given as a string), return the count of each atom.

An atomic element always starts with an uppercase character, then zero or more lowercase letters, representing the name.

1 or more digits representing the count of that element may follow if the count is greater than 1. If the count is 1, no digits will follow. For example, H2O and H2O2 are possible, but H1O2 is impossible.

Two formulas concatenated together produce another formula. For example, H2O2He3Mg4 is also a formula.

A formula placed in parentheses, and a count (optionally added) is also a formula. For example, (H2O2) and (H2O2)3 are formulas.

Given a formula, output the count of all elements as a string in the following form: the first name (in sorted order), followed by its count (if that count is more than 1), followed by the second name (in sorted order), followed by its count (if that count is more than 1), and so on.

Example 1:

Input: 
formula = "H2O"
Output: "H2O"
Explanation: 
The count of elements are {'H': 2, 'O': 1}.

 

Example 2:

Input: 
formula = "Mg(OH)2"
Output: "H2MgO2"
Explanation: 
The count of elements are {'H': 2, 'Mg': 1, 'O': 2}.

 

Example 3:

Input: 
formula = "K4(ON(SO3)2)2"
Output: "K4N2O14S4"
Explanation: 
The count of elements are {'K': 4, 'N': 2, 'O': 14, 'S': 4}.

 

Note:

  • All atom names consist of lowercase letters, except for the first character which is uppercase.
  • The length of formula will be in the range [1, 1000].
  • formula will only consist of letters, digits, and round parentheses, and is a valid formula as defined in the problem.

 

這道題給了我們一個化學式,讓我們數其中原子的個數。比如水是H2O,里面有兩個氫原子,一個氧原子,返回還是H2O。例子2給的是氫氧化鎂(哈哈,想不到這么多年過去了,高中化學還沒有完全還給老師,呀,暴露年齡了呢|||-.-),里面有一個鎂原子,氧原子和氫原子各兩個,我們返回H2MgO2,可以看到元素是按字母順序排列的,這道題就是純粹玩字符串,不需要任何的化學知識。再看第三個例子K4(ON(SO3)2)2,就算你不認識里面的鉀,硫,氮,氧等元素,也不影響做題,這個例子的返回是K4N2O14S4,鉀原子有4個,氮原子有2個,氧原子有14個,是3x2x2 + 2 = 14得來的,硫原子有4個,是2x2 = 4得來的。那么我們可以發現規律,先統計括號里的原子個數,然后如果括號外面有數字,那么括號里每個原子的個數乘以外面的數字即可,然后在外層若還有數字,那么就繼續乘這個數字,這種帶有嵌套形式的字符串,比較適合用遞歸來做。我們最終的目的是統計每個原子的數量,所以我們只要建立了每個元素和其出現次數的映射,就可以生成返回的字符串了,由於需要按元素的字母順序排列,所以我們使用TreeMap來建立映射。我們使用一個變量pos,來記錄我們遍歷的位置,這是個全局的變量,在遞歸函數參數中需要設置引用。我們遍歷的時候,需要分三種情況討論,分別是遇到左括號,右括號,和其他。我們一個個來看:

如果當前是左括號,那么我們pos先自增1,跳過括號位置,然后我們可以調用遞歸函數,來處理這個括號中包括的所有內容,外加上后面的數字,比如Mg(OH)2,在pos=2處遇到左括號,調用完遞歸函數后pos指向了最后一個字符的后一位,即pos=7。而在K4(ON(SO3)2)2中,如果是遇到中間的那個左括號pos=5時,調用完遞歸函數后pos指向了第二個右括號,即pos=11。遞歸函數返回了中間部分所有原子跟其個數之間的映射,我們直接將其都加入到當前的映射中即可。

如果當前是右括號,說明一個完整的括號已經遍歷完了,我們需要取出其后面的數字,如果括號存在,那么后面一定會跟數字,否則不需要括號。所以我們先讓pos自增1,跳過括號的位置,然后用個變量i記錄當前位置,再進行while循環,找出第一個非數字的位置,那么中間就都是數字啦,用substr將其提取出來,並轉為整數,然后遍歷當前的映射對,每個值都乘以這個倍數即可,然后返回。

如果當前是字母,那么需要將元素名提取出來了,題目中說了元素名只有第一個字母是大寫,后面如果有的話,都是小寫字母。所以我們用個while循環找到第一個非小寫字母的位置,用substr取出中間的字符串,即元素名。由於元素名后也可能跟數字,所以在用個while循環,來找之后第一個非數字的位置,用substr提取出數字字符串。當然也可能元素名后沒有數字,提取出來的數字字符串就是空的,我們加的時候判斷一下,如果為空就只加1,否則就加上轉化后的整數,參見代碼如下:

 

解法一:

class Solution {
public:
    string countOfAtoms(string formula) {
        string res = "";
        int pos = 0;
        map<string, int> m = parse(formula, pos);
        for (auto a : m) {
            res += a.first + (a.second == 1 ? "" : to_string(a.second));
        }
        return res;
    }
    map<string, int> parse(string& str, int& pos) {
        map<string, int> res;
        while (pos < str.size()) {
            if (str[pos] == '(') {
                ++pos;
                for (auto a : parse(str, pos)) res[a.first] += a.second;
            } else if (str[pos] == ')') {
                int i = ++pos;
                while (pos < str.size() && isdigit(str[pos])) ++pos;
                int multiple = stoi(str.substr(i, pos - i));
                for (auto a : res) res[a.first] *= multiple;
                return res;
            } else {
                int i = pos++;
                while (pos < str.size() && islower(str[pos])) ++pos;
                string elem = str.substr(i, pos - i);
                i = pos;
                while (pos < str.size() && isdigit(str[pos])) ++pos;
                string cnt = str.substr(i, pos - i);
                res[elem] += cnt.empty() ? 1 : stoi(cnt);
            }
        }
        return res;
    }
};

 

下面這種解法是迭代形式,根據上面的遞歸解法改寫而來。使用棧來代替遞歸函數,本身之上基本沒有任何區別。需要注意的是,在遇到左括號時,我們將當前映射集cur加入了棧,這里用了個自帶的move函數,表示將cur中所有的映射對移出並加入棧,之后cur就為空了。還有就是在處理右括號時,算出了倍數后,我們把當前的映射值乘以倍數后加到棧頂映射集中,然后用棧頂映射集來更新cur,並移除棧頂元素,參見代碼如下:

 

解法二:

class Solution {
public:
    string countOfAtoms(string formula) {
        string res = "";
        stack<map<string, int>> st;
        map<string, int> cur;
        int n = formula.size(), pos = 0;
        while (pos < n) {
            if (formula[pos] == '(') {
                ++pos;
                st.push(move(cur));
            } else if (formula[pos] == ')') {
                int i = ++pos;
                while (pos < n && isdigit(formula[pos])) ++pos;
                int multiple = stoi(formula.substr(i, pos - i));
                for (auto a : cur) st.top()[a.first] += a.second * multiple;
                cur = move(st.top());
                st.pop();
            } else {
                int i = pos++;
                while (pos < n && islower(formula[pos])) ++pos;
                string elem = formula.substr(i, pos - i);
                i = pos;
                while (pos < n && isdigit(formula[pos])) ++pos;
                string cnt = formula.substr(i, pos - i);
                cur[elem] += cnt.empty() ? 1 : stoi(cnt);
            }
        }
        for (auto a : cur) {
            res += a.first + (a.second == 1 ? "" : to_string(a.second));
        }
        return res;
    }
};

 

類似題目:

Decode String

Encode String with Shortest Length

Parse Lisp Expression

 

參考資料:

https://leetcode.com/problems/number-of-atoms/discuss/109328/C++-iterative-solution

https://leetcode.com/problems/number-of-atoms/discuss/112740/Concise-C++-recursive-parser

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM