Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
You are given a binary string string.
Write a script to re-arrange the given binary string that all occurrences of “01” are simultaneously replaced with “10” until no occurrences of “01” exist. Finally return the total steps needed.
For this task, I have coded the instructions as stated. I have a variable count
that counts the number of times the loop is run before the solution is found.
def rearrange_binstr(input_string: str) -> int:
if not re.search("^[01]+$", input_string):
raise ValueError("Invalid input")
count = 0
while "01" in input_string:
count += 1
input_string = re.sub("01", "10", input_string)
return count
The Perl solution follows the same logic.
sub main ($input_string) {
if ( $input_string !~ /^[01]+$/ ) {
die "Invalid input\n";
}
my $count = 0;
while ( index( $input_string, "01" ) != -1 ) {
$count++;
$input_string =~ s/01/10/g;
}
say $count;
}
$ ./ch-1.py 111000
0
$ ./ch-1.py 00011
4
$ ./ch-1.py 01011
3
$ ./ch-1.py 010101
3
$ ./ch-1.py 00001
4
You are given a chemical formula with elements, numbers, and parentheses.
Write a script to count the total number of each type of atom by expanding all grouped multipliers. Then, format and return the final inventory as a single string sorted alphabetically by element name, including the total count only if it is greater than 1.
For this task I start with a function called expand_parens
which will expand the inner most parentheses followed by a number using that number match_obj[2]
to multiple the number of atoms for each individual element match_obj[1]
.
def expand_parens(match_obj: re.Match) -> str:
multiplier = int(match_obj[2])
new_elements = defaultdict(int)
for elements in re.findall(r"([A-Z][a-z]?)(\d+)?", match_obj[1]):
new_elements[elements[0]] += (
int(1 if elements[1] == "" else elements[1]) * multiplier
)
return "".join(f"{key}{value}" for key, value in new_elements.items())
The main atom_count
function starts by expanding all parentheses are expanded.
def atom_count(input_string: str) -> str:
while re.search(r"\([A-Z0-9]+\)\d+", input_string, flags=re.I):
input_string = re.sub(
r"\(([A-Z0-9]+)\)(\d+)", expand_parens, input_string, flags=re.I
)
It then checks that the final string is as expected, in case the input has mismatched parentheses or symbols for example.
if not re.search("^[A-Z0-9]+$", input_string, flags=re.I):
raise ValueError("Invalid input")
The next step is to count the number of atoms each element has.
count_elements = defaultdict(int)
for elements in re.findall(r"([A-Z][a-z]?)(\d+)?", input_string):
count_elements[elements[0]] += int(1 if elements[1] == "" else elements[1])
The final step is to convert this dict into a string sorted alphabetically by the element. If there is more than one atom, the number is appended as required.
output = ""
for element in sorted(count_elements):
count = count_elements[element]
output += element
if count > 1:
output += str(count)
return output
Like with the first task, the Perl solution follows the same logic. It uses the s///e
pattern to call the expand_parens()
function.
$ ./ch-2.py "((N2O)3(H2O)2)2"
H8N12O10
$ ./ch-2.py "Mg3(PO4)2"
Mg3O8P2
$ ./ch-2.py "(((H)2)3)4"
H24
$ ./ch-2.py "NaCl3(O2(S10)2)2Mg"
Cl3MgNaO4S40
$ ./ch-2.py "Z2Y3(X2W)2"
W2X4Y3Z2