Nim
Very messy bruteforce.
I've had some problems with parsing in part 2 - I didn't account for double digit numbers before dna sequences and that caused my code to work on example, but silently fail only on the real input. I've figured it out after ~30 minutes with some external help.
Part 3 runs in 700ms - not great, but not too bad either.
proc similarity(a, b: string): int =
for i, c in a:
if c == b[i]: inc result
proc solve_part1*(input: string): Solution =
var sim: seq[int]
var dnaList: seq[string]
for line in input.splitLines():
dnaList.add line[2..^1]
for i in 0 .. dnaList.high:
for j in i+1 .. dnaList.high:
let s = similarity(dnaList[i], dnaList[j])
sim.add s
sim.sort()
result := sim[^2] * sim[^1]
proc parentTest(ch, p1, p2: string): bool =
for i, c in ch:
if (c != p1[i]) and (c != p2[i]): return false
true
proc simTable(dnaList: seq[string]): seq[seq[int]] =
result = newSeqWith(dnaList.len, newseq[int](dnaList.len))
for i in 0 .. dnaList.high:
for j in i+1 .. dnaList.high:
let s = similarity(dnaList[i], dnaList[j])
result[i][j] = s
result[j][i] = s
proc solve_part2*(input: string): Solution =
var dnaList: seq[string]
for line in input.splitLines():
dnaList.add line.split(':')[1]
let sim = simTable(dnaList)
var indices = toseq(0..dnaList.high)
for i, childDna in dnaList:
var indices = indices
indices.del i
block doTest:
for k in 0 .. indices.high:
for j in k+1 .. indices.high:
let p1 = indices[k]
let p2 = indices[j]
if parentTest(childDna, dnaList[p1], dnaList[p2]):
result.intVal += sim[i][p1] * sim[i][p2]
break doTest
proc solve_part3*(input: string): Solution =
var dnaList: seq[string]
for line in input.splitLines():
dnaList.add line.split(':')[1]
var families: seq[set[int16]]
var indices = toseq(0..dnaList.high)
for ch, childDna in dnaList:
var indices = indices
indices.del ch
block doTest:
for k in 0 .. indices.high:
for j in k+1 .. indices.high:
let p1 = indices[k]
let p2 = indices[j]
if parentTest(childDna, dnaList[p1], dnaList[p2]):
families.add {ch.int16, p1.int16, p2.int16}
break doTest
var combined: seq[set[int16]]
while families.len > 0:
combined.add families.pop()
var i = 0
while i <= families.high:
if (combined[^1] * families[i]).len > 0:
combined[^1] = combined[^1] + families[i]
families.del i
i = 0
else: inc i
let maxInd = combined.mapIt(it.len).maxIndex
result := combined[maxInd].toseq.mapIt(it.int+1).sum()
Full solution at Codeberg: solution.nim