ETOOBUSY 🚀 minimal blogging for the impatient
PWC102 - Hash-counting String
TL;DR
On with TASK #2 from the Perl Weekly Challenge #102. Enjoy!
The challenge
You are given a positive integer
$N
. Write a script to produce Hash-counting string of that length.The definition of a hash-counting string is as follows:
- the string consists only of digits 0-9 and hashes, ‘#’
- there are no two consecutive hashes: ‘##’ does not appear in your string
- the last character is a hash
- the number immediately preceding each hash (if it exists) is the position of that hash in the string, with the position being counted up from 1
It can be shown that for every positive integer N there is exactly one such length-N string.
The questions
The formulation of the challenge contains everything that is needed,
only I would have re-added that the end string must be of length $N
in the list of characteristics that define what a hash-counting string
is.
Also… it would have been a plus to have a link to the demonstration that such a string can always be produced. I tried a very superficial search, but to no avail.
The solution
The key to solving this challenge, for me, relies in these two characteristics:
- the last character is a hash
- there are no two consecutive hashes
The first one basically tells us what should appear at the end: a hash. It’s a start! Ehr… it’s an end! 🙄
The other one tells us that we have to put the 1-starting position
number, expressed as a decimal integer, immediately before, with the
possible exception of $N
being 1 because in this case we would have
already exhausted the characters that we have to fill in.
So, if we have 123456789 as an input, we know that the hash-counting
string MUST end with the sequence 123456789#
.
How long is this sequence? It’s 1 character for the #
, plus the
length of the input number, that is length $N
in Perl terms.
At this point, we are left to figure out what to put before this last
part of the string. It MUST be a string that is $N - (1 + length $N)
characters long, because we already filled in 1 + length $N
characters
at the end of the final string.
Wait a minute… we now need to fill in a $N_1 = $N - (1 + length $N)
string with the rules for hash-counting strings, so we can just repeat
our reasoning!
This will land us with new values for $N_x
that are always decreasing,
so we have to stop when we hit 1 or less (which should only be 0, if the
demonstration is correct). That 1 case is a special one, because it’s
the only situation where we put a #
without puttting an integer
before.
All in all, here’s a possible solution:
#!/usr/bin/env perl
use 5.024;
use warnings;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;
sub hash_counting_string ($N) {
my $retval = '';
($retval, $N) = ("$N#$retval", $N - 1 - length $N) while $N > 1;
return $N == 1 ? "#$retval" : $retval;
}
my $n = shift || 10;
say hash_counting_string($n);
I opted for a more compact and admittedly less readable solution because it becomes so short that it’s not difficult to figure out… like leaving a small challenge for the casual reader 😅
Considering that I don’t need to keep $N
and all its “derivative”
values $N_x
around, I just use it over and over to keep track of how
many characters I still have to put in the string. This is why $N
is
updated at each loop, just like the $retval
string.
Have a good day everyone!