See the original problem on HackerRank.
Solutions
The brute-force approach to solve this problems involves generating all the possible substrings of the input number. This requires \(O(N^2)\) time, which becomes too expensive in the worst case when the input size can be as large as \(2 \cdot 10^5\).
To find an efficient solution, let’s consider the input 8314.
All the possible substrings are listed below:
|  |  | 
Each line contains all the substrings ending with the corresponding digit. For example, the second line 3 83 includes all substrings ending with 3. The final result is the sum of all these substrings:
|  |  | 
To compute this efficiently, let’s focus on how each line can be calculated. For example:
|  |  | 
Notice that the digits from earlier lines “shift” left in subsequent lines. For instance, 8 becomes 80, then 800. Shifting corresponds to multiplying by 10. Thus, each digit contributes in two ways:
- directly, as a standalone value (e.g., 8,3);
- indirectly, by extending all substrings from previous digits.
For a digit at position i, the total contribution is the number of substrings it ends (\(i+1\)) multiplied by its value, plus the contributions from extending all substrings ending at the previous digit (scaled up by 10).
Using this, the formula to calculate the sum for each line is:
\( line[i+1] = (i + 1) \cdot digits[i+1] + 10 \cdot line[i] \)
With \(line[0] = digits[0]\).
To solve the problem, we iterate through the input once, applying this formula. We should not forget to use modulo arithmetic as required by the problem:
|  |  | 
An equivalent solution in Python:
|  |  | 
The solution is linear in time and constant in space.
This problem is actually similar to the Maximum Subarray problem and it is solved using the same pattern.
At this stage, we can represent this code using functional patterns by breaking down the algorithm into distinct steps:
- each character is mapped into its corresponding numeric digit;
- each digit contributes to calculating the next value;
- an accumulator is updated to maintain the total sum of all nextvalues.
Here is a possible result using STL:
|  |  | 
Unlike the previous solution, these steps are now executed sequentially, resulting in three passes over the input instead of a single pass. Also, additional space is allocated to store both digits and next values. Additionally, the use of partial_sum in this approach is not stateless, as the variable i is modified during each invocation of the lambda.
All such issues are resolved in the following solution implemented with Niebler’s range-v3 library, where enumerate is used to pair each element with its corresponding position (index):
|  |  | 
The first transform is straightforward: it simply converts characters into digits, just as in previous solution, but does so without requiring any additional space.
The most intricate part of this approach is the use of partial_sum. Essentially, partial_sum updates an accumulator while iterating through a range and allows access to the intermediate value of the accumulator at each step. Here, sum serves as the accumulator, with sum.second providing the current value of the accumulator, which was updated in the previous iteration. Meanwhile, cur represents the current input.
The syntax may appear convoluted because we’ve introduced enumerate to get the position of each element. As a result, each element becomes a pair, where the first value indicates the position, adding more complexity to the resulting solution.
Next, views::values is employed to extract only the second values from the pairs, effectively discarding the first values that represent the positions. This is because we are solely interested in the intermediate sums stored in the second values.
Finally, accumulate performs a straightforward summation of these intermediate sums, applying the modulo operation during the process.
This solution has linear time complexity, processes the input in a single pass, and requires no additional space.
A more concise implementation in Python using the same patterns is as follows:
|  |  | 
To keep the code simpler, we have omitted the modulo operations.
Quick breakdown:
- itertools.accumulateperforms the partial sum operation, akin to- views::partial_sum
- enumeratefunctions like- views::enumerate, adding positional indices to the values
- map(int, n)mirrors the role of- views::transform, converting characters to digits
- map(itemgetter(1), tuples)corresponds to- views::values, extracting only the second element of each tuple
- finally, sumserves the same purpose asaccumulate, summing up the values.
A similar solution by Simone Busoli in Javascript:
|  |  |