🎄 Advent of Code 2'023 - Day 01

Day 01: Trebuchet?!

* Cf. aoc. d. i xxiii

To solve the first day’s challenge, we need to find the first and last digit of each line, combine them, and then sum them all up with the result of the other lines. *

1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet

Given the input 1abc2: 1 and 2 are the first and last digits, respectively. So we put them together and we get 12. We do this for each line, and then we sum them all up.


PARS I

tip

I’m going to be honest with you, It was quite a challenge to solve this problem. I’m not proficient in C++. I’m actually learning it as we go through the challenge, so I had to do a lot of research in between the solutions.

I’ve started by solving the problem for a single line. The logic is quite simple: we iterate over the line, and if the current character is a digit, we check if the first digit is empty. If it is, we assign the current character to it.

α

We always update the last digit with the current character, so at the end of the iteration, we’ll have the first and last digits of the line.

That looks good! We can solve the problem for a single line. Now we need to make sure we can solve it for multiple lines. One way to do that is to calculate and reset the first and last variables after each new line \n.

β

#include<iostream>
#include<algorithm>

using namespace std;

class Solution {
public:
    static int part1(string &input) {
        int ans = 0;
        char first = ' ';
        char last = ' ';

        for (char &i: input) {
            // Reset first and last when we find a new line
            if (i == '\n') {
                if (first != ' ' && last != ' ') {
                    ans = (ans + (first - '0') * 10 + (last - '0'));
                }
                first = ' ';
                last = ' ';
                continue;
            }

            if (i >= '0' && i <= '9') {
                if (first == ' ') first = i;
                last = i;
            };
        }

        ans = (ans + (first - '0') * 10 + (last - '0'));

        return ans;
    }
};

In that way we can solve the problem for multiple lines in O(n).

With that we unlock the second part of the challenge and won our first star! 🌟


PARS II

In the second part of the challenge, we discovered that the digit can actually be spelled out with words. So both 1 and one are valid digits.

The way I decided to go about it was to create a map with all the spelled out digits and their respective values * containing the numeric digit.

* Some people were having trouble with the overlapping digits. For example twone should be a match for 2 and 1. That means that we can’t just replace the whole spelled out digit, we need to leave the chars that can be used to form other digits.

 unordered_map<string, string> C = {
     {"one",   "o1e"},
     {"two",   "t2o"},
     {"three", "t3e"},
     {"four",  "f4r"},
     {"five",  "f5e"},
     {"six",   "s6x"},
     {"seven", "s7n"},
     {"eight", "e8t"},
     {"nine",  "n9e"},
 };

Now we can iterate over the map and replace the spelled out digits with their respective numeric digits.

for (auto &i: C) {
   unsigned long pos = input.find(i.first);
   while (pos != string::npos) {
       input.replace(pos, i.first.size(), i.second);
       pos = input.find(i.first);
   }
}

Now we can use the same logic as before to find the first and last digits of each line and sum them up.

#include<iostream>
#include<algorithm>

using namespace std;

class Solution {
public:
    static int part1(string &input) {
        int ans = 0;
        char first = ' ';
        char last = ' ';

        for (char &i: input) {
            if (i == '\n') {
                if (first != ' ' && last != ' ') {
                    ans = (ans + (first - '0') * 10 + (last - '0'));
                }
                first = ' ';
                last = ' ';
                continue;
            }

            if (i >= '0' && i <= '9') {
                if (first == ' ') first = i;
                last = i;
            };
        }

        ans = (ans + (first - '0') * 10 + (last - '0'));

        return ans;
    }

    static int part2(string &input) {
        unordered_map<string, string> C = {
            {"one",   "o1e"},
            {"two",   "t2o"},
            {"three", "t3e"},
            {"four",  "f4r"},
            {"five",  "f5e"},
            {"six",   "s6x"},
            {"seven", "s7n"},
            {"eight", "e8t"},
            {"nine",  "n9e"},
        };

        for (auto &i: C) {
            unsigned long pos = input.find(i.first);
            while (pos != string::npos) {
                input.replace(pos, i.first.size(), i.second);
                pos = input.find(i.first);
            }
        }

        return part1(input);
    }
};

And with that we unlock the second star! 🌟🌟

Click here to access the blogpost from @fefas


α) we could also try a two pointer approach to find the first and the last digits. One pointer goes from right to left right++, and the other goes left to right left--. They both would stop once they found a digit and don't overlap with each other.

β) using a two pointers approach would make the solution more confusing and harder to understand, because we would need to track the single digits with the left pointer, and the double digits with the right pointer.