57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
import re
|
|
with open('day19-input', 'r') as file:
|
|
replacements, _, medicine_molecule = file.read().strip().partition('\n\n')
|
|
replacements = replacements.strip().split('\n')
|
|
replacements = [(old, new) for old, _, new in (rep.partition(' => ') for rep in replacements)]
|
|
medicine_molecule = medicine_molecule.strip()
|
|
|
|
|
|
unique_molecules = set()
|
|
for old, new in replacements:
|
|
r = re.compile(old)
|
|
for match in r.finditer(medicine_molecule):
|
|
unique_molecules.add(medicine_molecule[:match.start()] + new + medicine_molecule[match.end():])
|
|
|
|
print(f'Part 1: {len(unique_molecules)}')
|
|
|
|
|
|
starting_molecule = 'e'
|
|
replacements_ordered = sorted(replacements, key=lambda x: (len(x[1]), x[1])) # Shortest output to longest output
|
|
regexes_ordered = [(old, re.compile(new)) for old, new in replacements_ordered]
|
|
biggest_reduction = max((len(new) - len(old) for old, new in replacements_ordered))
|
|
print(f'Biggest reducing step is {biggest_reduction}')
|
|
|
|
# stack = [(0, medicine_molecule)] # We will work backwards from medicine to 'e' as it's easier to not make garbage
|
|
stack = {medicine_molecule: 0} # We will work backwards from medicine to 'e' as it's easier to not make garbage
|
|
best = 500 # Set a hard limit to prevent any potential cycles going forever
|
|
best_molecules = {}
|
|
|
|
while stack:
|
|
# Retrieve state
|
|
# replacements, molecule = stack.pop()
|
|
molecule, replacements = stack.popitem()
|
|
# Don't process if this is already worse
|
|
if replacements >= best:
|
|
continue
|
|
# If the best case can't reduce enough, abort
|
|
target_reduction = len(molecule) - 1
|
|
min_replacements = -(-target_reduction//biggest_reduction) # ceildiv
|
|
if replacements + min_replacements > best:
|
|
continue
|
|
# If we have worked back to the starting molecule, record this result
|
|
if molecule == starting_molecule:
|
|
best = replacements
|
|
print(f'New best found: {best}')
|
|
continue
|
|
|
|
r1 = replacements + 1
|
|
for old, r in regexes_ordered:
|
|
for match in r.finditer(molecule):
|
|
s = molecule[:match.start()] + old + molecule[match.end():]
|
|
if s not in best_molecules or best_molecules[s] < r1:
|
|
best_molecules[s] = r1
|
|
# stack.append((r1, s))
|
|
stack[s] = r1
|
|
|
|
print(f'Part 2: fewest replacements = {best}')
|