↓快轉到主要內容
  1. LeetCode/

LeetCode 726: Number of Atoms

·2 分鐘· ·
LeetCode Hard Stack String Hash-Map Sorting
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-07-11

  • 提交紀錄:Wrong Answer ×3 → Accepted

解題思路
#

化學式有三種東西:

  • 元素:一個大寫字母,後面接 0 個以上的小寫字母,例如 H、Mg。
  • 數字:可能有好幾位,沒寫就是 1。
  • 括號:( ... ) 後面可以接一個倍數,沒寫就是 1。

用 index 從左到右解析,每次讀一個完整的 token:

  1. 讀到 (:在 stack 放一個標記。
  2. 讀到 ):讀出後面的倍數,把 stack 裡到 ( 為止的元素計數都乘上倍數,再放回去。
  3. 讀到大寫字母:讀完整個元素名稱和後面的數字,推進 stack。

最後把 stack 裡的計數加總,依元素名稱排序輸出。

我的嘗試
#

前三版都是「一個字元一個字元處理」,邊讀邊猜目前的字元屬於哪個 token,錯在好幾個邊界:

  1. digits = "23456789":漏了 0 和 1。題目說數量 1 不會寫出來,但 10、21 這種多位數還是會出現 0 和 1。
  2. 數字只讀一位:H12 會被讀成 H1 和 2。
  3. ) 後面沒有數字:例如 (OH),括號裡的元素沒有被合併,) 標記一直留在 stack 裡。

第四版改成用 index 一次讀完一個完整的 token,每種 token 各自處理,就 AC 了。

正確解法
#

from collections import defaultdict

class Solution:
    def countOfAtoms(self, formula: str) -> str:
        n = len(formula)
        i = 0

        def read_number() -> int:
            nonlocal i
            start = i
            while i < n and formula[i].isdigit():
                i += 1
            return int(formula[start:i]) if i > start else 1

        stack = [defaultdict(int)]
        while i < n:
            ch = formula[i]
            if ch == "(":
                stack.append(defaultdict(int))
                i += 1
            elif ch == ")":
                i += 1
                mult = read_number()
                inner = stack.pop()
                for atom, cnt in inner.items():
                    stack[-1][atom] += cnt * mult
            else:
                start = i
                i += 1
                while i < n and formula[i].islower():
                    i += 1
                atom = formula[start:i]
                stack[-1][atom] += read_number()

        counts = stack[0]
        return "".join(a + (str(counts[a]) if counts[a] > 1 else "") for a in sorted(counts))

這裡讓 stack 的每一層是一個 dict,) 時把這一層乘上倍數合併到上一層。AC 版是把 (元素, 數量) 一筆一筆放在同一個 stack 上,遇到 ) 時往回彈到 ( 標記,概念一樣。

複雜度
#

時間 O(n^2) 最壞(巢狀很深時,每層合併都要掃過裡面所有元素),加上排序。空間 O(n)。

要特別避免的錯誤
#

  • 數字字元集合漏掉 0 和 1。
  • 數字只讀一位。
  • ) 後面沒有數字時沒有預設倍數 1。
  • 一個字元一個字元猜 token 的類型:先切成完整的 token 再處理,邏輯清楚很多。

面試口說整理
#

我用 index 從左到右解析,每次讀一個完整的 token:元素名稱是一個大寫加上後面的小寫,數字可能有好幾位、沒寫就是 1。stack 的每一層是一個計數的 dict,遇到 ( 開新的一層,遇到 ) 讀出倍數,把這一層乘上倍數後併回上一層。最後把元素排序輸出,數量大於 1 才寫數字。

相關文章

LeetCode 488: Zuma Game
·2 分鐘
LeetCode Hard Dynamic-Programming Memoization Breadth-First-Search String Stack
LeetCode 488 解題筆記:board 最多 16 顆、hand 最多 5 顆,把 (board, hand) 整個當狀態做 DFS + 記憶化;只在「同色球前面」和「拆開兩顆同色球」兩種位置插入
LeetCode 3816: Lexicographically Smallest String After Deleting Duplicate Characters
·1 分鐘
LeetCode Hard Stack Monotonic-Stack Greedy String
LeetCode 3816 解題筆記:單調棧,頂端比較大而且還有別的副本就刪;最後把尾巴重複的也刪掉,因為前綴一定比較小
LeetCode 30: Substring with Concatenation of All Words
·2 分鐘
LeetCode Daily Hard String Sliding-Window Two-Pointers Hash-Map Brute-Force Frequency-Counting
三種作法:暴力、計數優化、偏移量滑動視窗。
LeetCode 76: Minimum Window Substring
·1 分鐘
LeetCode Daily Hard String Sliding-Window Variable-Window Two-Pointers Hash-Map
LeetCode 解題紀錄 - 可變大小滑動窗口與字符計數
LeetCode 1745: Palindrome Partitioning IV
·1 分鐘
LeetCode Hard Dynamic-Programming Interval-Dp String
LeetCode 1745 解題筆記:先用區間 DP 建好 is_pal[i][j],再枚舉兩個切點,三段都是回文就成立,O(n^2)
LeetCode 2122: Recover the Original Array
·2 分鐘
LeetCode Hard Two-Pointers Sorting Hash-Table Enumeration Array
LeetCode 2122 解題筆記:最小的數一定在 lower,枚舉它的配對決定 k;排序後用兩個指針把每個最小的未配對數配給 +2k 的數