Advent of code 2015/19
Ajax Direct

Answer

Part 1 :
Part 2 :
[$replacements, $molecule] = $input->split("\n\n");
$replacements = $replacements->lines->map(fn ($line) => $line->split(" => "));

function get_switches($molecule, $replacements)
{
    $switches = [];
    foreach ($replacements as [$from, $to]) {
        foreach ($molecule->searches($from) as $pos) {
            $chars = clone $molecule->chars();
            $chars->splice($pos, strlen($from), [$to]);
            $switches[] = $chars->join("");
        }
    }
    return set($switches)->unique();
}

// ==================================================
// > PART 1
// ==================================================
$solution_1 = get_switches($molecule, $replacements)->count();

// ==================================================
// > PART 2
// ==================================================
$replacements = $replacements->map(fn ($a) => [$a[1], $a[0]]);

$graph = (new Graph(function ($graph) use ($replacements) {
    if (strlen($graph->current) < 2) return [];
    $switches = get_switches(scalar($graph->current), $replacements);
    return array_fill_keys((array) $switches, 1);
}));

$graph->definePriority(function ($graph, $state, $value) {
    return levenshtein($state, $graph->end);
});

$solution_2 = $graph->explore($molecule->string, "e")[1];