Skip to content

Commit

Permalink
Solution for Day 14 of 2021.
Browse files Browse the repository at this point in the history
  • Loading branch information
eamonnmcmanus committed Dec 15, 2024
1 parent bc9f3e5 commit f813248
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 1 deletion.
1 change: 0 additions & 1 deletion src/advent2021/Puzzle13.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
Expand Down
125 changes: 125 additions & 0 deletions src/advent2021/Puzzle14.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package advent2021;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableMap.toImmutableMap;

import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;

/**
* @author Éamonn McManus
*/
public class Puzzle14 {
private static final String SAMPLE =
"""
NNCB
CH -> B
HH -> N
CB -> H
NH -> C
HB -> C
HC -> B
HN -> C
NN -> C
BH -> H
NC -> B
NB -> B
BN -> B
BB -> N
BC -> B
CC -> N
CN -> C
""";

private static final Map<String, Callable<Reader>> INPUT_PRODUCERS =
ImmutableMap.of(
"sample", () -> new StringReader(SAMPLE),
"problem",
() -> new InputStreamReader(Puzzle1.class.getResourceAsStream("puzzle14.txt")));

public static void main(String[] args) throws Exception {
for (var entry : INPUT_PRODUCERS.entrySet()) {
String name = entry.getKey();
try (Reader r = entry.getValue().call()) {
List<String> lines = CharStreams.readLines(r);
String input = lines.get(0);
checkArgument(lines.get(1).isEmpty());
Pattern rulePattern = Pattern.compile("([A-Z][A-Z]) -> ([A-Z])");
ImmutableMap<String, Character> substitutions =
lines.stream()
.skip(2)
.map(rulePattern::matcher)
.peek(m -> checkState(m.matches()))
.collect(toImmutableMap(m -> m.group(1), m -> m.group(2).charAt(0)));

System.out.printf(
"Result for %s after 10 steps is is %d\n", name, polymerize(input, 10, substitutions));
System.out.printf(
"Result for %s after 40 steps is is %d\n", name, polymerize(input, 40, substitutions));
}
}
}

// For Part 1, a naïve string-building approach is fine, but the sizes are obviously much too big
// in Part 2. But we don't need to know what the actual string is, only what letter pairs are in
// it, and we only need to maintain a count for each of those. At each step, if we have a letter
// pair AC that gets B inserted into it, and if there are N occurrences of AC, then for the next
// step we will have N times AB and N times BC.
private static long polymerize(String input, int steps, Map<String, Character> substitutions) {
char first = input.charAt(0);
char last = input.charAt(input.length() - 1);
Map<String, Long> pairFrequencies = new LinkedHashMap<>();
for (int j = 1; j < input.length(); j++) {
String pair = input.substring(j - 1, j + 1);
pairFrequencies.put(pair, pairFrequencies.getOrDefault(pair, 0L) + 1);
}
for (int i = 0; i < steps; i++) {
Map<String, Long> newPairFrequencies = new LinkedHashMap<>();
pairFrequencies.forEach(
(pair, count) -> {
Character insert = substitutions.get(pair);
if (insert == null) {
// Turns out this doesn't happen, but never mind.
newPairFrequencies.put(pair, newPairFrequencies.getOrDefault(pair, 0L) + count);
} else {
String pair1 = "" + pair.charAt(0) + insert;
newPairFrequencies.put(pair1, newPairFrequencies.getOrDefault(pair1, 0L) + count);
String pair2 = "" + insert + pair.charAt(1);
newPairFrequencies.put(pair2, newPairFrequencies.getOrDefault(pair2, 0L) + count);
}
});
pairFrequencies = newPairFrequencies;
}
Map<Character, Long> letterFrequencies = new LinkedHashMap<>();
pairFrequencies.forEach(
(pair, count) -> {
Character c1 = pair.charAt(0);
letterFrequencies.put(c1, letterFrequencies.getOrDefault(c1, 0L) + count);
Character c2 = pair.charAt(1);
letterFrequencies.put(c2, letterFrequencies.getOrDefault(c2, 0L) + count);
});
// We've counted every letter twice, once when it was the start of the pair it is in and
// once when it was the end, except for the very first and last letters of the string. Those
// letters don't change. So we divide all the values by 2 and then add 1 for the first and
// last letters.
for (Character letter : letterFrequencies.keySet()) {
letterFrequencies.put(letter, letterFrequencies.get(letter) / 2);
}
letterFrequencies.put(first, letterFrequencies.get(first) + 1);
letterFrequencies.put(last, letterFrequencies.get(last) + 1);
long max = Collections.max(letterFrequencies.values());
long min = Collections.min(letterFrequencies.values());
return max - min;
}
}
102 changes: 102 additions & 0 deletions src/advent2021/puzzle14.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
SCVHKHVSHPVCNBKBPVHV

SB -> B
HH -> P
VF -> N
BS -> S
NC -> C
BF -> H
BN -> H
SP -> H
BK -> H
FF -> N
VN -> B
FN -> C
FS -> S
PP -> F
ON -> H
FV -> F
KO -> F
PK -> H
VB -> S
HS -> B
NV -> O
PN -> S
VH -> B
OS -> P
BP -> H
OV -> B
HK -> S
NN -> K
SV -> C
PB -> F
SK -> F
FB -> S
NB -> K
HF -> P
FK -> K
KV -> P
PV -> F
BC -> S
FO -> N
HC -> F
CP -> B
KK -> F
PC -> S
HN -> O
SH -> H
CK -> P
CO -> F
HP -> K
PS -> C
KP -> F
OF -> K
KS -> F
NO -> V
CB -> K
NF -> N
SF -> F
SC -> P
FC -> V
BV -> B
SS -> O
KC -> K
FH -> C
OP -> C
CF -> K
VO -> V
VK -> H
KH -> O
NP -> V
NH -> O
NS -> V
BH -> C
CH -> S
CC -> F
CS -> P
SN -> F
BO -> S
NK -> S
OO -> P
VV -> F
FP -> V
OK -> C
SO -> H
KN -> P
HO -> O
PO -> H
VS -> N
PF -> N
CV -> F
BB -> H
VC -> H
HV -> B
CN -> S
OH -> K
KF -> K
HB -> S
OC -> H
KB -> P
OB -> C
VP -> C
PH -> K

0 comments on commit f813248

Please sign in to comment.