1. 程式人生 > >【LeetCode】726. Number of Atoms 解題報告(Python)

【LeetCode】726. Number of Atoms 解題報告(Python)

題目描述:

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.

題目大意

給出了一個化學分式,計算裡面的原子個數是多少,並且按照原子字母的遞增有序輸出。

解題方法

方法一:DFS

這個題第一眼就看到了括號,立馬想到了括號匹配問題。括號匹配問題使用一個記數指標,遇到左括號加一,遇到右括號減一,如果該記數指標等於0了,說明找到了匹配的括號。在這個題中就相當於找到了一個分子團,該分子團後面會有個數字,代表這個分子團出現的次數。所以,做法就是如果不是分子團,那麼統計元素的個數;如果是分子團,那麼把這個分子團當做分子,計算裡面元素的個數再乘以外邊的分子團的個數。所以就是個DFS問題。

比較難辦的就是尋找每個元素,需要根據大小寫和數字等判斷;尋找個數,需要把字串轉成10進位制。最後把分子式內的元素個數×分子式的個數的時候,按照元素迭代的方式做,不要使用對分子式個數的for迴圈去累加。

最壞的時間複雜度是O(N!),最優時間複雜度是O(N),空間複雜度是O(N)。其中N是分子的長度。

class Solution(object):
    def countOfAtoms(self, formula):
        """
        :type formula: str
        :rtype: str
        """
        count = self.dfs(formula)
        res = ""
        for atom, num in sorted(count.items()):
            if num == 1:
                res += atom
            else:
                res += atom + str(num)
        return res
        
    def dfs(self, formula):
        count = collections.Counter()
        if not formula: return count
        i = 0
        while i < len(formula):
            if formula[i].isalpha(): # 首字母是英文字元
                atom = formula[i]
                atomNum = 0
                # 找到這個元素所有字元
                i += 1
                while i < len(formula) and formula[i].isalpha() and formula[i].islower():
                    atom += formula[i]
                    i += 1
                while i < len(formula) and formula[i].isdigit(): # 後面是否有數字
                    atomNum = 10 * atomNum + int(formula[i])
                    i += 1
                count[atom] += 1 if atomNum == 0 else atomNum # 使用加號
            elif formula[i] == "(": # 括號匹配
                left = i # 左括號位置
                parent = 1 # 統計括號個數
                while i < len(formula) and parent != 0:
                    i += 1
                    if formula[i] == "(":
                        parent += 1
                    elif formula[i] == ")":
                        parent -= 1
                right = i
                atomNum = 0
                i += 1
                while i < len(formula) and formula[i].isdigit(): # 後面是否有數字
                    atomNum = 10 * atomNum + int(formula[i])
                    i += 1
                innerCount = self.dfs(formula[left + 1 : right])
                for c, n in innerCount.items():
                    count[c] += n * atomNum
        count += self.dfs(formula[i + 1 :])
        return count

方法二:棧

看到括號匹配,也會讓人立馬想到棧,其實DFS本身就是棧實現的,所以也完全可以用棧來解決。

方法是,左括號進棧,然後把字母依次進棧,當遇到右括號的時候,需要對棧進行退棧操作,這個時候要統計每個元素的次數,當退棧的時候遇到左括號,說明內部的分子團已經結束,那麼把遇到的第一個左括號退棧,把內部的分子團的各個元素和其個數進棧。然後遍歷就好了!這個方法的好處是,當最後遍歷結束的時候,棧裡面儲存的只剩下了已經統計好了的各個元素和其個數的對應,每個元素只會出現一次,相當於已經做了元素的求和操作,最後只需要排序即可。

為了方便,我把分子式用括號包了起來,方便棧操作的判斷。

這個題做了很久,主要是查一個bug,查了一個小時,感覺很詭異。其實仔細對比一下和上面DFS的解法,大同小異。區別是我用字母n儲存了分子式的長度,然後下面退棧的for迴圈中又使用了n這個變數名稱!!由於python不用宣告變數,所以直接把外邊的n覆蓋掉了!!做法很簡單,把內部for迴圈裡的變數名改一下就好了!生氣!!

最壞的時間複雜度是O(N!),最優時間複雜度是O(N),空間複雜度是O(N)。其中N是分子的長度。

程式碼如下:

class Solution(object):
    def countOfAtoms(self, formula):
        """
        :type formula: str
        :rtype: str
        """
        stack = list()
        formula = "(" +  formula + ")1"
        i = 0
        n = len(formula)
        
        while i < n:
            if i >= n: continue
            if formula[i] == "(":
                stack.append("(")
                i += 1
            elif formula[i] == ")":
                parentNum = 0
                i += 1
                while i < n and formula[i].isdigit():
                    parentNum = 10 * parentNum + int(formula[i])
                    i += 1
                count = collections.Counter()
                while stack[-1] != "(":
                    atom, atomNum = stack.pop()
                    count[atom] += atomNum * parentNum
                if stack[-1] == "(":
                    stack.pop()
                for c, t in count.items(): # 剛開始把變數t寫成了n!!錯了很多次
                    stack.append((c, t))
            elif formula[i].isalpha():
                atom = formula[i]
                atomNum = 0
                i += 1
                while i < n and formula[i].isalpha() and formula[i].islower():
                    atom += formula[i]
                    i += 1
                while i < n and formula[i].isdigit():
                    atomNum = 10 * atomNum + int(formula[i])
                    i += 1
                atomNum = 1 if atomNum == 0 else atomNum
                stack.append((atom, atomNum))
            
                    
        res = ""
        for atoms in sorted(stack):
            if atoms == "(":
                continue
            c, n = atoms
            if n == 1:
                res += c
            else:
                res += c + str(n)
        return res

參考資料:

日期

2018 年 10 月 4 日 —— 一個很不容易察覺的小錯誤,需要總結一下坑了!