Problem
Question
Given a string
formularepresenting a chemical formula, return the count of each atom.
The atomic element always starts with an uppercase character, then zero or more lowercase letters, representing the name.
One or more digits representing that element’s count 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 are concatenated together to 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.
Return 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.
The test cases are generated so that all the values in the output fit in a 32-bit integer.
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}.
Constraints:
1 <= formula.length <= 1000formulaconsists of English letters, digits,'(', and')'.formulais always valid.
Solutions
1. Reverse Traversal
Time Complexity: | Space Complexity:
To solve this problem, we can iterate from the end of the given string while keeping track of the total multiplier. To store the counts, we use a dictionary. To manage multipliers within parentheses, a stack is used. Generally, there are four conditions to handle:
-
When a digit is observed:
- Continue iterating to build the entire number.
- Save the number in
cur_mult.
-
When a ”)” is observed:
- Push
cur_multonto the stack. - Update
total_multby multiplying it bycur_mult. - Reset
cur_multto 1.
- Push
-
When a ”(” is observed:
- Pop the stack.
- Use the popped value to update
total_multby dividing it by this value.
-
When a character is observed:
- Continue iterating to find the entire chemical element (until the character is not lowercase).
- Update the dictionary counter by adding the result of multiplying
cur_multwithtotal_mult. - Reset
cur_mult.`
class Solution:
def countOfAtoms(self, formula: str) -> str:
stack = []
total_mult = 1
cur_mult = 1
memo = defaultdict(int)
i = len(formula) - 1
while i >= 0:
cur = formula[i]
if cur == ")":
stack.append(cur_mult)
total_mult *= cur_mult
cur_mult = 1
elif cur == "(":
old_mult = stack.pop()
total_mult /= old_mult
elif cur.isdigit():
number = 0
multiplier = 1
while i >= 0 and formula[i].isdigit():
number += multiplier * int(formula[i])
multiplier *= 10
i -= 1
cur_mult = number if number != 0 else 1
continue
else:
if cur.islower():
while i >= 0 and formula[i].islower():
cur = formula[i - 1] + cur
i -= 1
memo[cur] += (total_mult * cur_mult)
cur_mult = 1
i -= 1
keys = list(memo)
keys.sort()
result = ''
for key in keys:
number = str(int(memo[key])) if memo[key] > 1 else ''
result = result + key + number
return result
2. Recursive Approach
Time Complexity: | Space Complexity:
In this method, most of the logic is similar, but we iterate forward. We call a helper function each time we encounter new parentheses. The recursive function returns a dictionary containing all the inner counts. We then multiply all these values by the number that follows the parentheses, effectively distributing the multiplier to all nested elements.
class Solution:
def countOfAtoms(self, formula: str) -> str:
def iterate(fr):
memo = defaultdict(int)
n = len(formula)
while fr < n:
cur = formula[fr]
if cur == "(":
sub_memo, fr = iterate(fr + 1)
number = 0
while fr < n and formula[fr].isdigit():
number = number * 10 + int(formula[fr])
fr += 1
if number == 0: number = 1
for key in sub_memo:
memo[key] += sub_memo[key] * number
elif cur == ")":
return memo, fr + 1
else:
if cur.isalpha() and cur.isupper():
key = cur
fr += 1
while fr < n and formula[fr].isalpha() and formula[fr].islower():
key += formula[fr]
fr += 1
number = 0
while fr < n and formula[fr].isdigit():
number = number * 10 + int(formula[fr])
fr += 1
if number == 0: number = 1
memo[key] += number
return memo, n
memo, _ = iterate(0)
result = []
for atom in sorted(memo.keys()):
count = memo[atom]
if count == 1:
result.append(atom)
else:
result.append(atom + str(count))
return ''.join(result)