from collections import Counter def sum_counts(counts): """ >>> sum_counts([('a', 20), ('b', 10), ('a', 30)]) {'a': 50, 'b': 10} """ final = Counter() for k, v in counts: final[k] += v return final polymer, eqs = text.split('\n\n') mapping = dict(eq.split(' -> ') for eq in eqs.splitlines()) grow = {(a, b): ((a, x), (x, b)) for (a, b), x in mapping.items()} duos = Counter(duo for duo in zip(polymer, polymer[1:])) scores = {} for n in range(40): duos = sum_counts((new, N) for old, N in duos.items() for new in grow[old]) # account for double-counting when scoring, except at edges singles = sum_counts((k, N) for ab, N in duos.items() for k in ab) singles[polymer[0]] += 1 singles[polymer[-1]] += 1 xmax, *_, xmin = [N // 2 for _, N in singles.most_common()] scores[n + 1] = xmax - xmin ans1 = scores[10] ans2 = scores[40]