141 lines
4.8 KiB
C++
141 lines
4.8 KiB
C++
#include <algorithm>
|
|
#include <expected>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <print>
|
|
#include <ranges>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
struct IDRange {
|
|
long leftRange;
|
|
long rightRange;
|
|
};
|
|
|
|
auto parseRanges(const std::string &filename) -> std::expected<std::vector<IDRange>, std::string> {
|
|
std::ifstream inputF{filename};
|
|
|
|
if (!inputF) {
|
|
return std::unexpected{"Some file open error.\n"};
|
|
}
|
|
std::vector<IDRange> idRanges{};
|
|
|
|
std::string bigString{};
|
|
std::getline(inputF, bigString);
|
|
auto ranges = std::ranges::to<std::vector<std::string>>(std::views::split(bigString, ','));
|
|
|
|
for (const auto &range : ranges) {
|
|
auto subParts = std::ranges::to<std::vector<std::string>>(std::views::split(range, '-'));
|
|
idRanges.emplace_back(std::stol(subParts[0]), std::stol(subParts[1]));
|
|
}
|
|
|
|
inputF.close();
|
|
|
|
return std::move(idRanges);
|
|
}
|
|
|
|
auto countDoubles(const std::vector<IDRange> &idRanges) {
|
|
long doubles{};
|
|
for (const auto &rng : idRanges) {
|
|
// std::println("{}-{}", rng.leftRange, rng.rightRange);
|
|
for (long i = rng.leftRange; i <= rng.rightRange; i++) {
|
|
auto str_version = std::to_string(i);
|
|
auto str_len = str_version.size();
|
|
// std::print("Checking {}. Length: {}", str_version, str_version.size());
|
|
if (str_len % 2 == 0) {
|
|
// std::print(" Divisible by two, splitting...");
|
|
std::string_view left_half = std::string_view(str_version).substr(0, str_len / 2);
|
|
std::string_view right_half = std::string_view(str_version).substr(str_len / 2, str_len);
|
|
// std::print("Halves: {}/{}",left_half, right_half);
|
|
if (left_half == right_half) {
|
|
// std::print(" EQUAL!");
|
|
doubles += i;
|
|
}
|
|
}
|
|
// std::println();
|
|
}
|
|
}
|
|
return doubles;
|
|
}
|
|
|
|
auto calculatePossibleSplits(int length) -> std::vector<int> {
|
|
// std::println("Possible ways to split a str of length {}: ", length);
|
|
std::vector<int> returnVec{};
|
|
for (auto toCheck : std::views::iota(2, length + 1)) {
|
|
if (length % toCheck == 0) {
|
|
// std::println("{}", toCheck);
|
|
returnVec.push_back(toCheck);
|
|
}
|
|
}
|
|
return std::move(returnVec);
|
|
}
|
|
std::unordered_map<int, std::vector<int>> splitMap{};
|
|
auto getPossibleSplits(int length) -> const std::vector<int> & {
|
|
if (!splitMap.contains(length)) {
|
|
// std::println("Caching {}",length);
|
|
splitMap[length] = calculatePossibleSplits(length);
|
|
}
|
|
return splitMap.at(length);
|
|
}
|
|
|
|
auto countRepeats(const std::vector<IDRange> &idRanges) {
|
|
long doubles{};
|
|
for (const auto &rng : idRanges) {
|
|
// std::println("{}-{}", rng.leftRange, rng.rightRange);
|
|
for (long i = rng.leftRange; i <= rng.rightRange; i++) {
|
|
auto str_version = std::to_string(i);
|
|
auto str_len = str_version.size();
|
|
// std::print("Checking {}. Length: {} ", str_version, str_version.size());
|
|
|
|
const std::vector<int> &numsToCheck = getPossibleSplits(str_len);
|
|
// std::println("Possible splits: {}", numsToCheck);
|
|
for (int checkNum : numsToCheck) {
|
|
// std::print("Splitting in {}: ", checkNum);
|
|
std::vector<std::string_view> splits(checkNum);
|
|
for (auto toCheck : std::views::iota(0, checkNum)) {
|
|
auto window = str_len / checkNum;
|
|
splits[toCheck] = std::string_view(str_version).substr(toCheck * window, window);
|
|
// std::print("[{}]: {} /{} ", toCheck, splits[toCheck], window);
|
|
}
|
|
|
|
if (std::equal(splits.begin() + 1, splits.end(), splits.begin())) {
|
|
// std::print(" EQUAL!");
|
|
doubles += i;
|
|
break;
|
|
}
|
|
|
|
// std::println();
|
|
}
|
|
// std::println();
|
|
}
|
|
// std::println();
|
|
}
|
|
return doubles;
|
|
}
|
|
|
|
auto main() -> int {
|
|
|
|
auto testCase = parseRanges("test_input");
|
|
if (testCase) {
|
|
auto testResult = countDoubles(*testCase);
|
|
auto testResultP2 = countRepeats(*testCase);
|
|
std::println("P1 Testcase result: {}", testResult);
|
|
std::println("P2 Testcase result: {}", testResultP2);
|
|
} else {
|
|
std::print("{}\n", testCase.error());
|
|
}
|
|
|
|
auto realRanges = parseRanges("puzzle_input");
|
|
if (realRanges) {
|
|
// auto realResult = countDoubles(*realRanges);
|
|
auto realResultP2 = countRepeats(*realRanges);
|
|
// std::println("P1 Real result: {}", realResult);
|
|
std::println("P2 Real result: {}", realResultP2);
|
|
} else {
|
|
std::print("{}\n", realRanges.error());
|
|
}
|
|
|
|
return 0;
|
|
}
|