LeetCode: Number of Atoms
This question is asked relatively commonly at both TikTok and Google. Here given the chemical formula as a string, return the count of each atom. The atomic element always starts with an uppercase character then followed by zero or more lowercase letters. One or more digits representing the element's count follow if the count is greater than 1. If a count is 1, no digits will follow since H2O and H2O2 are possible but not H1O2. Two formulas can be concatenated together to produce another formula, H2O2He3Mg4 is also a formula and a formula placed in parentheses is also a formula. Given a formula we want to return all the count of the elements as a string as a first name, followed by its count, followed by the second name, followed by its count, and so on.
Here are explanations and formulas:
H2O is the count of elements {H: 2, O: 1} and Mg(OH)2 with the output of H2MgO2 sing there are 2 H elements, etc. So we want to perform recursion, store the names inside of an ArrayList, and finally, sort these and spit the numbers out in numerical and sequential order. The first approach is recursion, and this means when we see a bracket, parse whatever is in the bracket otherwise we see an uppercase character and parse the rest of the letters. Then if there is a final multiplicity, we'll multiply our answer by this.
Here's the first solution, commented line by line:
class Solution {
//this is a marker of the character in the particular word.
int i;
/*
* This function basically takes in a compound and returns the number of atoms in a particular element in each compound.
* @parameters: Formula Compound
* @returns in alphabetical order a compound and the number of elements of an atom in this particular compound
*/
public String countOfAtoms(String formula) {
//this indicates a new Stringbuilder
StringBuilder ans = new StringBuilder();
//start at the beginning of the string.
i = 0;
//have a list of elements and the number of atoms
Map<String, Integer> count = parse(formula);
//concatenate string and number in order
for (String name: count.keySet()) {
ans.append(name);
int multiplicity = count.get(name);
if(multiplicity > 1) ans.append("" + multiplicity);
}
//return string with elements and count of element.
return new String(ans);
}
/*
* This function parses through and generates a map between the element and the count of the element
* @parameters: Formula Compound
* @return a map of an element and number of atoms inside the element combination.
*/
public Map<String, Integer> parse(String formula) {
//length of the entire equation
int N = formula.length();
//hashmap
Map<String, Integer> count = new TreeMap();
// go until the first ')' sign
while(i < N && formula.charAt(i) != ')') {
// if we see the first parentheses
if(formula.charAt(i) == '(') {
i++;
//for every map entry recursively parse things inside the entry, parse the rest of the letters.
for(Map.Entry<String, Integer> entry: parse(formula).entrySet()) {
count.put(entry.getKey(), count.getOrDefault(entry.getKey(), 0) + entry.getValue());
} else {
//get to the beginning of the substring.
int iStart = i++;
//advance further if there are lowercase characters.
while(i < N && Character.isLowerCase(formula.charAt(i))) i++;
int multiplicity = iStart < i ? Integer.parseInt(formula.substring(iStart, i)) : 1;
count.put(name, count.getOrDefault(name, 0) + multiplicity);
}
}
//start at the character after the initial few ones
int iStart = ++i;
//go through the count of the character amount
while(i < N && Character.isDigit(formula.charAt(i)))) i++;
//go through all of the characters
if(iStart < i) {
//parse through the integer of the substring as the multiplier
int multiplicity = Integer.parseInt(formula.substring(iStart, i));
//multiply all the numbers by this factor
for(String key : count.keySet()) {
count.put(key, count.get(key) * multiplicity);
}
}
//return the given hashmap.
return count;
}
}
The third algorithm is the regular expression algorithm. We can use regular expressions whenever parsing. We want to match uppercase letters followed by any number of lowercase letters than digits or matching a left bracket following a right bracket. If we parse the name with the letters and numbers, add this into the current count, and do the same if we parsed the left bracket. If we parse a left bracket, we append a count to the stack, but if we parse the right bracket we multiply the deepest level count and add these entries to the current count. Here's the final code:
//regex library
import java.util.regex.*;
class Solution {
/*
* This function takes in a compound and returns the number of atoms in a particular element in each compound.
* @parameters: Formula Compound
* @returns in alphabetical order a compound and the number of elements of an atom in this particular compound
*/
public String countOfAtoms(String formula) {
//element nume with number and parentheses.
Matcher matcher = Pattern.compile("([A-Z][a-z]*)(\\d*)|(\\()|(\\ (\\d*)").matcher(formula);
//initialize a stack of HashMap
Stack<Map<String, Integer>> stack = new Stack();
//push an empty HashMap onto the stack
stack.push(new TreeMap());
while(matcher.find()) {
String match = matcher.group();
//see if there is a match what opens
if(match.equals("(")) {
//push a new element inside of the stack
stack.push(new TreeMap());
//this represents the end of a character.
} else if(match.startsWith(")")) {
//get top element of the stack
Map<String, Integer> top = stack.pop();
//find the number multiplied afterwards
int multiplicity = match.length() > 1 ? : Integer.parseInt(match.substring(1, match.length())) : 1;
//take the element in the stack and multiply these.
for(String name: top.keySet()) {
stack.peek().put(name, stack.peek().getOrDefault(name, 0) + top.get(name * multiplicity);
}
//case where there is no parentheses.
} else {
//start over again. This match is the combination of letters and numbers. We parse the String and integer here.
int i = 1;
//iterate through all the lower case characters
while(i < match.length() && Character.isLowerCase(match.charAt(i))) {
i++;
}
//get the name of the element.
String name = match..substring(0, i);
//see the number of elements here through parsing.
int multiplicity = i < match.length() ? Integer.parseInt(match.substring(i, match.length())) : 1;
//add this into the hashmap content on the current string iteration.
stack.peek().put(name, stack.peek().getOrDefault(name, 0) + multiplicity);
}
}
//build the final String by sorting all of them and putting it into a string.
StringBuilder ans = new StringBuilder();
for(String name: stack.peek().keySet()) {
ans.append(name);
final int count = stack.peek().get(name);
//only append the count if the count is greater than 1, per question.
if(count > 1) ans.append(String.valueOf(count));
}
//return the final string
return ans.toString();
}
}


Comments
Post a Comment