import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.stream.Collectors;

class AoC_2018_05 extends Solution {
    static final int DAY = 5;
    static final String TITLE = "Advent of Code 2018 Day 5: Alchemical Reduction";

    public static void main(String[] args) {
        System.out.println(TITLE);

        // Tests
        testStringsShouldEliminate();
        testReducePolymer();

        // Solve
        String puzzleInput = loadPuzzleInputLines(DAY).collect(Collectors.joining());
        int partOneResult = solvePartOne(puzzleInput);
        assert partOneResult == 11546;
        System.out.printf("Part one: %d\n", partOneResult);
    }

    static int solvePartOne(String puzzleInput) {
        return reducePolymer(puzzleInput).length();
    }

    static String reducePolymer(String polymer) {
        ArrayDeque<String> stack = new ArrayDeque<>(polymer.length());
        for (String c : polymer.split("")) {
            String stackTop = stack.peekLast();
            if (stackTop != null && stringsShouldEliminate(c, stackTop)) {
                stack.removeLast();
            } else {
                stack.add(c);
            }
        }
        StringBuilder builder = new StringBuilder();
        for (String c : stack) {
            builder.append(c);
        }
        return builder.toString();
    }

    static boolean stringsShouldEliminate(String first, String second) {
        return first.equalsIgnoreCase(second) && !first.equals(second);
    }

    static void testReducePolymer() {
        String input = "dabAcCaCBAcCcaDA";
        String expected = "dabCBAcaDA";
        String result = reducePolymer(input);
        assert result.equals(expected) : result;
    }

    static void testStringsShouldEliminate() {
        assert stringsShouldEliminate("a", "A");
        assert !stringsShouldEliminate("a", "a");
        assert stringsShouldEliminate("C", "c");
        assert !stringsShouldEliminate("C", "C");
    }

}

@@ -43,16 +43,65 @@
 til.
-Iterator
+Arrays;%0Aimport java.util.Comparator;%0Aimport java.util.Set
 ;%0Aim
@@ -450,16 +450,77 @@
 lymer();
+%0A        testUniqueCharacters();%0A        testBestReduction();
 %0A%0A      
@@ -766,24 +766,139 @@
 OneResult);%0A
+        int partTwoResult = solvePartTwo(puzzleInput);%0A        System.out.printf(%22Part two: %25d%5Cn%22, partTwoResult);%0A
     %7D%0A%0A    s
@@ -993,32 +993,141 @@
 ength();%0A    %7D%0A%0A
+    static int solvePartTwo(String puzzleInput) %7B%0A        return bestReduction(puzzleInput).length();%0A    %7D%0A%0A
     static Strin
@@ -1823,32 +1823,593 @@
 second);%0A    %7D%0A%0A
+    static Set%3CString%3E uniqueCharacters(String input) %7B%0A        return Arrays.stream(input.split(%22%22))%0A            .map(s -%3E s.toLowerCase())%0A            .collect(Collectors.toSet());%0A    %7D%0A%0A    static String bestReduction(String input) %7B%0A        Set%3CString%3E characters = uniqueCharacters(input);%0A        return characters.stream()%0A            .map(character -%3E input.replaceAll(character, %22%22).replaceAll(character.toUpperCase(), %22%22))%0A            .map(AoC_2018_05::reducePolymer)%0A            .min(Comparator.comparing(String::length))%0A            .get();%0A    %7D%0A%0A
     static void 
@@ -2867,10 +2867,457 @@
 %0A    %7D%0A%0A
+    static void testUniqueCharacters() %7B%0A        assert uniqueCharacters(%22abcABC%22).equals(Set.of(%22a%22, %22b%22, %22c%22));%0A        assert uniqueCharacters(%22A%22).equals(Set.of(%22a%22)); // lowercases%0A        assert uniqueCharacters(%22BC%22).equals(Set.of(%22b%22, %22c%22)); // lowercases%0A    %7D%0A%0A    static void testBestReduction() %7B%0A        String input = %22dabAcCaCBAcCcaDA%22;%0A        String expected = %22daDA%22;%0A        assert bestReduction(input).equals(expected);%0A    %7D%0A%0A
 %7D%0A

Solve part two