From 9ef280959375341007bf7ca1ddd4788cf8b3633c Mon Sep 17 00:00:00 2001 From: Thraix Date: Wed, 29 Jul 2026 00:30:29 +0200 Subject: [PATCH] Add solution for 2015 Day 19 --- fetch.sh | 2 +- res/2015/day19/test_input.txt | 7 + src/2015/Day19.cpp | 259 ++++++++++++++++++++++++++++++++-- src/common/lib/Helper.h | 9 +- 4 files changed, 265 insertions(+), 12 deletions(-) diff --git a/fetch.sh b/fetch.sh index ed89a36..165bcbd 100755 --- a/fetch.sh +++ b/fetch.sh @@ -13,7 +13,7 @@ for item in $list; do dir="res/$year/day$day/input.txt" if [ ! -e $dir ]; then webitem=${day#0} - echo "Fetching $dir" + echo "Fetching $dir from https://adventofcode.com/$year/day/$webitem/input" tmp_file=$(mktemp) HTTP_CODE=$(curl -s -w "%{http_code}" -o "$tmp_file" --cookie cookies.txt https://adventofcode.com/$year/day/$webitem/input) if [ "$HTTP_CODE" -eq 200 ]; then diff --git a/res/2015/day19/test_input.txt b/res/2015/day19/test_input.txt index e69de29..70905c4 100644 --- a/res/2015/day19/test_input.txt +++ b/res/2015/day19/test_input.txt @@ -0,0 +1,7 @@ +e => H +e => O +H => HO +H => OH +O => HH + +HOHOHO diff --git a/src/2015/Day19.cpp b/src/2015/Day19.cpp index ee9906c..90c714b 100644 --- a/src/2015/Day19.cpp +++ b/src/2015/Day19.cpp @@ -2,30 +2,271 @@ namespace y2015::day19 { - REGISTER_DAY(2015, Day19, std::vector, int); + struct Input + { + std::multimap conversions; + std::string molecule; + }; - REGISTER_TEST_EXAMPLE(2015, Day19, ExampleInput, 1, 0); - REGISTER_TEST(2015, Day19, Input, 1, 0); - REGISTER_TEST_EXAMPLE(2015, Day19, ExampleInput, 2, 0); - REGISTER_TEST(2015, Day19, Input, 2, 0); + REGISTER_DAY(2015, Day19, Input, int); + + REGISTER_TEST_EXAMPLE(2015, Day19, ExampleInput, 1, 7); + REGISTER_TEST(2015, Day19, Input, 1, 576); + // REGISTER_TEST_EXAMPLE(2015, Day19, ExampleInput, 2, 6); + REGISTER_TEST(2015, Day19, Input, 2, 207); READ_INPUT(input) { - std::vector vec; + Input i; std::string str; while (getline(input, str)) { + if (str.empty()) + break; + + std::string from; + std::string to; + std::stringstream ss{str}; + ss >> from >> "=>" >> to; + i.conversions.emplace(from, to); } - return vec; + getline(input, i.molecule); + return i; + } + + std::vector FindAll(const std::string& str, const std::string& find) + { + std::vector positions; + size_t pos = str.find(find); + while (pos != std::string::npos) + { + positions.emplace_back(pos); + pos = str.find(find, pos + 1); + } + return positions; + } + + bool IsAtom(const std::string& sv) + { + std::regex regex{"^(e|[A-Z][a-z]?)$"}; + return std::regex_match(sv, regex); + } + + std::string Prettify(const std::string& molecule) + { + std::string result = molecule; + for (size_t i = 0; i < result.size(); i++) + { + if (Helper::StartsWith(result, "Rn", i)) + { + result.replace(i, 2, "("); + i--; + } + else if (Helper::StartsWith(result, "Ar", i)) + { + result.replace(i, 2, ")"); + i--; + } + else if (Helper::StartsWith(result, "Y", i)) + { + result.replace(i, 1, ","); + } + } + return result; + } + + std::pair Solve(const std::string& molecule, + const std::multimap& conversions, + std::map>& memoization); + + void Solve(std::string& molecule, + int& steps, + size_t& offset, + size_t pos, + size_t size, + const std::multimap& conversions, + std::map>& memoization) + { + std::string toReduce = molecule.substr(pos, size); + auto [solveStr, solveSteps] = Solve(toReduce, conversions, memoization); + Helper::Replace(molecule, toReduce.size(), solveStr, pos); + steps += solveSteps; + offset -= toReduce.size() - solveStr.size(); + } + + void SolveRnAr(std::string& molecule, + int& steps, + const std::multimap& conversions, + std::map>& memoization) + { + // Solve all parts between Rn and Ar + int stack = 0; + size_t position = 0; + for (size_t i = 0; i < molecule.size(); i++) + { + if (Helper::StartsWith(molecule, "Rn", i)) + { + if (stack == 0) + position = i + 2; + i++; + stack++; + } + else if (Helper::StartsWith(molecule, "Ar", i)) + { + stack--; + if (stack == 0) + { + Solve(molecule, steps, i, position, i - position, conversions, memoization); + } + i++; + } + } + } + + bool SolveY(std::string& molecule, + int& steps, + const std::multimap& conversions, + std::map>& memoization) + { + // Reduce all parts split by Y + int stack = 0; + size_t position = 0; + bool hasY = false; + for (size_t i = 0; i < molecule.size(); i++) + { + if (Helper::StartsWith(molecule, "Rn", i)) + stack++; + else if (Helper::StartsWith(molecule, "Ar", i)) + stack--; + + if (Helper::StartsWith(molecule, "Y", i)) + { + if (stack != 0) + continue; + + hasY = true; + Solve(molecule, steps, i, position, i - position, conversions, memoization); + position = i + 1; + } + } + if (hasY) + { + size_t i = 0; + Solve(molecule, steps, i, position, molecule.size(), conversions, memoization); + return true; + } + return false; + } + + void SolveAr(std::string& molecule, + int& steps, + const std::multimap& conversions, + std::map>& memoization) + { + // Reduce all parts ending with Ar (except the last one) + for (size_t i = 0; i < molecule.size(); i++) + { + if (Helper::StartsWith(molecule, "Ar", i) && i != molecule.size() - 2) + { + Solve(molecule, steps, i, 0, i + 2, conversions, memoization); + } + } + } + + std::pair SolveToAtom(const std::string& molecule, + const std::multimap& conversions, + std::map>& memoization) + { + if (IsAtom(molecule)) + return {molecule, 0}; + + auto it = memoization.find(molecule); + if (it != memoization.end()) + return it->second; + + int steps = std::numeric_limits::max(); + std::string result = "."; + for (const auto& [from, to] : conversions) + { + std::vector positions = FindAll(molecule, from); + for (const auto& pos : positions) + { + std::string moleculeCpy = molecule; + moleculeCpy.replace(pos, from.size(), to); + auto [solveStr, solveSteps] = SolveToAtom(moleculeCpy, conversions, memoization); + if (solveStr == ".") + continue; + if (solveSteps < steps) + { + steps = solveSteps; + result = solveStr; + } + } + } + + memoization[molecule] = {result, steps + 1}; + return {result, steps + 1}; + } + + void SolveToAtom(std::string& molecule, + int& steps, + const std::multimap& conversions, + std::map>& memoization) + { + // Reduce the rest + auto [solveStr, solveSteps] = SolveToAtom(molecule, conversions, memoization); + if (solveStr == ".") + { + std::cout << "No solution found for: " << molecule << std::endl; + } + Helper::Replace(molecule, molecule.size(), solveStr, 0); + steps += solveSteps; + } + + std::pair Solve(const std::string& molecule, + const std::multimap& conversions, + std::map>& memoization) + { + if (IsAtom(molecule)) + return {molecule, 0}; + + std::string result = molecule; + int steps = 0; + + SolveRnAr(result, steps, conversions, memoization); + if (SolveY(result, steps, conversions, memoization)) + return {result, steps}; + SolveAr(result, steps, conversions, memoization); + SolveToAtom(result, steps, conversions, memoization); + + return {result, steps}; } OUTPUT1(input) { - return 0; + std::set uniques; + for (const auto& [from, to] : input.conversions) + { + std::vector positions = FindAll(input.molecule, from); + for (const auto& pos : positions) + { + std::string str = input.molecule; + Helper::Replace(str, from.size(), to, pos); + uniques.emplace(str); + } + } + return uniques.size(); } OUTPUT2(input) { - return 0; + std::multimap reverse; + for (const auto& [from, to] : input.conversions) + { + reverse.emplace(to, from); + } + + std::map> memoization; + return Solve(input.molecule, reverse, memoization).second; } } diff --git a/src/common/lib/Helper.h b/src/common/lib/Helper.h index 7e60443..0d7cf41 100644 --- a/src/common/lib/Helper.h +++ b/src/common/lib/Helper.h @@ -49,9 +49,9 @@ struct Helper t.erase(std::remove_if(t.begin(), t.end(), func), t.end()); } - static bool StartsWith(const std::string_view& str, const std::string& prefix) + static bool StartsWith(const std::string_view& str, const std::string& prefix, size_t offset = 0) { - return str.substr(0, prefix.size()) == prefix; + return str.substr(offset, prefix.size()) == prefix; } static bool EndsWith(const std::string_view& str, const std::string& prefix) @@ -59,6 +59,11 @@ struct Helper return str.substr(str.size() - prefix.size(), prefix.size()) == prefix; } + static void Replace(std::string& str, size_t size, const std::string& other, size_t pos) + { + str.replace(pos, size, other); + } + static bool IsDigit(char c) { return c >= '0' && c <= '9';