[$polymer, $rules] = $input->split("\n\n");
$pairs = [];
for ($i = 0; $i < $polymer->length() - 1; $i++) {
    $pair = $polymer[$i] . $polymer[$i + 1];
    $pairs[$pair] = ($pairs[$pair] ?? 0) + 1;
}
$rules = $rules->lines->mapAssoc(function ($i, $rule) {
    [$from, $ins] = $rule->split(" -> ");
    return [(string) $from => [$from[0] . $ins, $ins . $from[1]]];
});
$solve = function($steps) use ($pairs, $rules) {
    for ($i = 0; $i < $steps; $i++) {
        $npairs = [];
        foreach ($pairs as $pair=>$count) {
            foreach ($rules[$pair] as $p) {
                $npairs[$p] = ($npairs[$p] ?? 0) + $count;
            }
        }
        $pairs = $npairs;
    }
    $occ = [];
    foreach ($pairs as $pair=>$c) {
        foreach (str_split($pair) as $l) $occ[$l] = ($occ[$l] ?? 0) + $c;
    }
    // Divide by 2, because each letter is counted in two pairs
    $occ = set($occ)->map(fn ($o) => ceil($o / 2));
    return $occ->max() - $occ->min();
};
$solution_1 = $solve(10);
$solution_2 = $solve(40);