Skip to main content

Number of Atoms

Problem Description​

Given a string formula representing 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.

Examples​

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

Constraints​

  • 1 <= formula.length <= 1000
  • formula consists of English letters, digits, '(', and ')'.
  • formula is always valid.

Solution for Number of Atoms​

Approach 1: Recursion​

Intuition and Algorithm​

Write a function parse that parses the formula from index i, returning a map count from names to multiplicities (the number of times that name is recorded).

We will put i in the global state: our parse function increments i throughout any future calls to parse.

  • When we see a '(', we will parse whatever is inside the brackets (up to the closing ending bracket) and add it to our count.

  • Otherwise, we should see an uppercase character: we will parse the rest of the letters to get the name, and add that (plus the multiplicity if there is one.)

  • At the end, if there is a final multiplicity (representing the multiplicity of a bracketed sequence), we'll multiply our answer by this.

Code in Different Languages​

Written by @Shreyash3087
#include <string>
#include <unordered_map>
#include <map>

class Solution {
private:
int i;

public:
std::string countOfAtoms(std::string formula) {
std::string ans;
i = 0;
std::map<std::string, int> count = parse(formula);
for (const auto& [name, multiplicity] : count) {
ans += name;
if (multiplicity > 1) {
ans += std::to_string(multiplicity);
}
}
return ans;
}

std::map<std::string, int> parse(std::string formula) {
int N = formula.length();
std::map<std::string, int> count;
while (i < N && formula[i] != ')') {
if (formula[i] == '(') {
i++;
for (const auto& [name, multiplicity] : parse(formula)) {
count[name] += multiplicity;
}
} else {
int iStart = i++;
while (i < N && islower(formula[i])) {
i++;
}
std::string name = formula.substr(iStart, i - iStart);
iStart = i;
while (i < N && isdigit(formula[i])) {
i++;
}
int multiplicity = iStart < i ? std::stoi(formula.substr(iStart, i - iStart)) : 1;
count[name] += multiplicity;
}
}
int iStart = ++i;
while (i < N && isdigit(formula[i])) {
i++;
}
if (iStart < i) {
int multiplicity = std::stoi(formula.substr(iStart, i - iStart));
for (auto& [name, count_] : count) {
count_ *= multiplicity;
}
}
return count;
}
};


Complexity Analysis​

Time Complexity: O(N2)O(N^2)​

Reason: where N is the length of the formula. It is O(N)O(N) to parse through the formula, but each of O(N)O(N) multiplicities after a bracket may increment the count of each name in the formula (inside those brackets), leading to an O(N2)O(N^2) complexity.

Space Complexity: O(N)O(N)​

Reason: We aren't recording more intermediate information than what is contained in the formula.

Approach 2: Stack​

Intuition and Algorithm​

Instead of recursion, we can simulate the call stack by using a stack of counts directly.

Code in Different Languages​

Written by @Shreyash3087
#include <iostream>
#include <stack>
#include <map>
#include <string>
#include <cctype>

class Solution {
public:
std::string countOfAtoms(std::string formula) {
int N = formula.length();
std::stack<std::map<std::string, int>> stack;
std::map<std::string, int> top_map;
stack.push(top_map);

for (int i = 0; i < N;) {
if (formula[i] == '(') {
std::map<std::string, int> new_map;
stack.push(new_map);
i++;
} else if (formula[i] == ')') {
std::map<std::string, int> top = stack.top();
stack.pop();
int iStart = ++i, multiplicity = 1;
while (i < N && std::isdigit(formula[i])) {
i++;
}
if (i > iStart) {
multiplicity = std::stoi(formula.substr(iStart, i - iStart));
}
for (auto it = top.begin(); it != top.end(); ++it) {
int v = it->second;
stack.top()[it->first] = stack.top().count(it->first) ? stack.top()[it->first] + v * multiplicity : v * multiplicity;
}
} else {
int iStart = i++;
while (i < N && std::islower(formula[i])) {
i++;
}
std::string name = formula.substr(iStart, i - iStart);
iStart = i;
while (i < N && std::isdigit(formula[i])) {
i++;
}
int multiplicity = i > iStart ? std::stoi(formula.substr(iStart, i - iStart)) : 1;
stack.top()[name] = stack.top().count(name) ? stack.top()[name] + multiplicity : multiplicity;
}
}

std::string ans;
for (auto it = stack.top().begin(); it != stack.top().end(); ++it) {
ans += it->first;
int multiplicity = it->second;
if (multiplicity > 1) {
ans += std::to_string(multiplicity);
}
}
return ans;
}
};


Complexity Analysis​

Time Complexity: O(N2)O(N^2)​

Reason: where N is the length of the formula. It is O(N)O(N) to parse through the formula, but each of O(N)O(N) multiplicities after a bracket may increment the count of each name in the formula (inside those brackets), leading to an O(N2)O(N^2) complexity.

Space Complexity: O(N)O(N)​

Reason: We aren't recording more intermediate information than what is contained in the formula.

References​


Authors:

Loading...