Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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.
"H2O" and "H2O2" are possible, but "H1O2" is impossible.Two formulas are concatenated together to produce another formula.
"H2O2He3Mg4" is also a formula.A formula placed in parentheses, and a count (optionally added) is also a formula.
"(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 <= 1000formula consists of English letters, digits, '(', and ')'.formula is always valid.Problem summary: 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
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Stack
"H2O"
"Mg(OH)2"
"K4(ON(SO3)2)2"
decode-string)encode-string-with-shortest-length)parse-lisp-expression)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #726: Number of Atoms
class Solution {
public String countOfAtoms(String formula) {
Map<String, Integer> map = new HashMap<>();
int[] stack = new int[1000];
int top = 0, multiplier = 1, freq = 0;
char[] c = formula.toCharArray();
for (int i = c.length - 1; i >= 0; i--) {
if (c[i] >= 'a' && c[i] <= 'z') {
int end = i--;
while (i >= 0 && c[i] >= 'a' && c[i] <= 'z') i--;
String key = new String(c, i, end - i + 1);
map.put(key, map.getOrDefault(key, 0) + Math.max(freq, 1) * multiplier);
freq = 0;
} else if (c[i] >= 'A' && c[i] <= 'Z') {
String key = new String(c, i, 1);
map.put(key, map.getOrDefault(key, 0) + Math.max(freq, 1) * multiplier);
freq = 0;
} else if (c[i] >= '0' && c[i] <= '9') {
freq = c[i] - '0';
int p = 10;
while (i - 1 >= 0 && c[i - 1] >= '0' && c[i - 1] <= '9') {
freq += p * (c[--i] - '0');
p *= 10;
}
} else if (c[i] == ')') {
stack[top++] = multiplier;
multiplier *= Math.max(freq, 1);
freq = 0;
} else {
multiplier = stack[--top];
}
}
List<String> keys = new ArrayList<>(map.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key);
int f = map.get(key);
if (f > 1) sb.append(f);
}
return sb.toString();
}
}
// Accepted solution for LeetCode #726: Number of Atoms
// Auto-generated Go example from java.
func exampleSolution() {
}
// Reference (java):
// // Accepted solution for LeetCode #726: Number of Atoms
// class Solution {
// public String countOfAtoms(String formula) {
// Map<String, Integer> map = new HashMap<>();
// int[] stack = new int[1000];
// int top = 0, multiplier = 1, freq = 0;
// char[] c = formula.toCharArray();
// for (int i = c.length - 1; i >= 0; i--) {
// if (c[i] >= 'a' && c[i] <= 'z') {
// int end = i--;
// while (i >= 0 && c[i] >= 'a' && c[i] <= 'z') i--;
// String key = new String(c, i, end - i + 1);
// map.put(key, map.getOrDefault(key, 0) + Math.max(freq, 1) * multiplier);
// freq = 0;
// } else if (c[i] >= 'A' && c[i] <= 'Z') {
// String key = new String(c, i, 1);
// map.put(key, map.getOrDefault(key, 0) + Math.max(freq, 1) * multiplier);
// freq = 0;
// } else if (c[i] >= '0' && c[i] <= '9') {
// freq = c[i] - '0';
// int p = 10;
// while (i - 1 >= 0 && c[i - 1] >= '0' && c[i - 1] <= '9') {
// freq += p * (c[--i] - '0');
// p *= 10;
// }
// } else if (c[i] == ')') {
// stack[top++] = multiplier;
// multiplier *= Math.max(freq, 1);
// freq = 0;
// } else {
// multiplier = stack[--top];
// }
// }
// List<String> keys = new ArrayList<>(map.keySet());
// Collections.sort(keys);
// StringBuilder sb = new StringBuilder();
// for (String key : keys) {
// sb.append(key);
// int f = map.get(key);
// if (f > 1) sb.append(f);
// }
// return sb.toString();
// }
// }
# Accepted solution for LeetCode #726: Number of Atoms
class Solution:
def countOfAtoms(self, formula: str) -> str:
def parse() -> dict:
ans = collections.defaultdict(int)
nonlocal i
while i < n:
if formula[i] == '(':
i += 1
for elem, freq in parse().items():
ans[elem] += freq
elif formula[i] == ')':
i += 1
numStart = i
while i < n and formula[i].isdigit():
i += 1
factor = int(formula[numStart:i])
for elem, freq in ans.items():
ans[elem] *= factor
return ans
elif formula[i].isupper():
elemStart = i
i += 1
while i < n and formula[i].islower():
i += 1
elem = formula[elemStart:i]
numStart = i
while i < n and formula[i].isdigit():
i += 1
num = 1 if i == numStart else int(
formula[numStart:i])
ans[elem] += num
return ans
n = len(formula)
ans = ""
i = 0
count = parse()
for elem in sorted(count.keys()):
ans += elem
if count[elem] > 1:
ans += str(count[elem])
return ans
// Accepted solution for LeetCode #726: Number of Atoms
struct Solution;
use std::collections::BTreeMap;
use std::iter::Peekable;
use std::str::Chars;
use std::vec::IntoIter;
#[derive(Debug)]
enum Tok {
Num(usize),
Name(String),
Op(char),
}
impl Solution {
fn count_of_atoms(formula: String) -> String {
let mut it = formula.chars().peekable();
let tokens = Self::parse_tokens(&mut it);
let count: BTreeMap<String, usize> = Self::parse(&mut tokens.into_iter().peekable());
count
.into_iter()
.map(|(k, v)| if v > 1 { format!("{}{}", k, v) } else { k })
.collect()
}
fn parse(it: &mut Peekable<IntoIter<Tok>>) -> BTreeMap<String, usize> {
let mut res: BTreeMap<String, usize> = BTreeMap::new();
loop {
match it.peek() {
Some(Tok::Name(_)) => {
if let Some(Tok::Name(s)) = it.next() {
let x = if let Some(&Tok::Num(x)) = it.peek() {
it.next();
x
} else {
1
};
*res.entry(s).or_default() += x;
}
}
Some(&Tok::Op('(')) => {
it.next();
let inside = Self::parse(it);
it.next();
let x = if let Some(&Tok::Num(x)) = it.peek() {
it.next();
x
} else {
1
};
for (k, v) in inside {
*res.entry(k).or_default() += v * x;
}
}
Some(&Tok::Op(')')) | None => {
break;
}
_ => panic!(),
}
}
res
}
fn parse_tokens(it: &mut Peekable<Chars>) -> Vec<Tok> {
let mut res = vec![];
while let Some(c) = it.next() {
match c {
'(' | ')' => res.push(Tok::Op(c)),
'0'..='9' => {
let mut x = (c as u8 - b'0') as usize;
while let Some('0'..='9') = it.peek() {
x *= 10;
let y = (it.next().unwrap() as u8 - b'0') as usize;
x += y;
}
res.push(Tok::Num(x));
}
'A'..='Z' => {
let mut s = "".to_string();
s.push(c);
while let Some('a'..='z') = it.peek() {
s.push(it.next().unwrap());
}
res.push(Tok::Name(s));
}
_ => panic!(),
}
}
res
}
}
#[test]
fn test() {
let formula = "H2O".to_string();
let res = "H2O".to_string();
assert_eq!(Solution::count_of_atoms(formula), res);
let formula = "Mg(OH)2".to_string();
let res = "H2MgO2".to_string();
assert_eq!(Solution::count_of_atoms(formula), res);
let formula = "K4(ON(SO3)2)2".to_string();
let res = "K4N2O14S4".to_string();
assert_eq!(Solution::count_of_atoms(formula), res);
}
// Accepted solution for LeetCode #726: Number of Atoms
function countOfAtoms(formula: string): string {
const getCount = (formula: string, factor = 1) => {
const n = formula.length;
const cnt: Record<string, number> = {};
const s: string[] = [];
let [atom, c] = ['', 0];
for (let i = 0; i <= n; i++) {
if (formula[i] === '(') {
const stk: string[] = ['('];
let j = i;
while (stk.length) {
j++;
if (formula[j] === '(') stk.push('(');
else if (formula[j] === ')') stk.pop();
}
const molecule = formula.slice(i + 1, j);
const nextFactor: string[] = [];
while (isDigit(formula[++j])) {
nextFactor.push(formula[j]);
}
const nextC = getCount(molecule, +nextFactor.join('') || 1);
for (const [atom, c] of Object.entries(nextC)) {
cnt[atom] = (cnt[atom] ?? 0) + c * factor;
}
i = j - 1;
continue;
}
if (s.length && (!formula[i] || isUpper(formula[i]))) {
[atom, c] = getAtom(s);
c *= factor;
cnt[atom] = (cnt[atom] ?? 0) + c;
s.length = 0;
}
s.push(formula[i]);
}
return cnt;
};
return Object.entries(getCount(formula))
.sort(([a], [b]) => a.localeCompare(b))
.map(([a, b]) => (b > 1 ? a + b : a))
.join('');
}
const regex = {
atom: /(\D+)(\d+)?/,
isUpper: /[A-Z]+/,
};
const getAtom = (s: string[]): [string, number] => {
const [_, atom, c] = regex.atom.exec(s.join(''))!;
return [atom, c ? +c : 1];
};
const isDigit = (ch: string) => !Number.isNaN(Number.parseInt(ch));
const isUpper = (ch: string) => regex.isUpper.test(ch);
Use this to step through a reusable interview workflow for this problem.
For each element, scan left (or right) to find the next greater/smaller element. The inner scan can visit up to n elements per outer iteration, giving O(n²) total comparisons. No extra space needed beyond loop variables.
Each element is pushed onto the stack at most once and popped at most once, giving 2n total operations = O(n). The stack itself holds at most n elements in the worst case. The key insight: amortized O(1) per element despite the inner while-loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Pushing without popping stale elements invalidates next-greater/next-smaller logic.
Usually fails on: Indices point to blocked elements and outputs shift.
Fix: Pop while invariant is violated before pushing current element.