Perl Weekly Challenge: Week 372
Challenge 1:
Rearrange Spaces
You are given a string text of words that are placed among number of spaces.
Write a script to rearrange the spaces so that there is an equal number of spaces between every pair of adjacent words and that number is maximised. If you can’t distribute, place the extra spaces at the end. Finally return the string.
Example 1
Input: $str = " challenge "
Output: "challenge "
We have 4 spaces and 1 word. So all spaces go to the end.
Example 2
Input: $str = "coding is fun"
Output: "coding is fun"
We have 4 spaces and 3 words (2 gaps). So 2 spaces per gap.
Example 3
Input: $str = "a b c d"
Output: "a b c d "
We have 4 spaces and 4 words (3 gaps). So 1 space per gap and 1 remainder.
Example 4
Input: $str = " team pwc "
Output: "team pwc"
We have 10 spaces and 2 words (1 gap). So 10 spaces per gap.
Example 5
Input: $str = " the weekly challenge "
Output: "the weekly challenge "
We have 9 spaces and 3 words (2 gaps). So 4 spaces per gap and 1 remainder.
First we split $str into a list of words with .words().
my @words = $str.words;
While we could count each space, an easier way is to subtract the sum of the
length of the words from the total length of $str.
my $spaces = $str.chars - @words».chars.sum;
The last piece of information neeeded is the number of gaps; which will always be one less than the number of worfd.
my $gaps = @words.elems - 1;
If there are gaps...
if $gaps {
... we divide the number of spaces by the number of gaps and add that many spaces to every word except the last one.
for 0 ..^ $gaps -> $i {
@words[$i] ~= q{ } x ($spaces / $gaps);
}
If the number of spaces did not divide into the number of gaps evenly, we add the
remainder to the end of the @words list.
@words.push(q{ } x ($spaces % $gaps));
If there were no gaps (i.e. there is only one word in @words,) we append all
the spaces to @words.
} else {
@words.push(q{ } x $spaces);
}
Now all the spaces will be properly distributed. We .join() all of @words
back to together into one string and wrap it in quotes for output.
say q{"}, @words.join, q{"};
The only replacement we need for the Perl version is sum(); other than that it
is a direct translation from Raku.
my @words = $str =~/(\w+)/g;
my $spaces = (length $str) - sum(map { length $_ } @words);
my $gaps = scalar @words - 1;
if ($gaps) {
for my $i (0 .. $gaps - 1) {
$words[$i] .= q{ } x ($spaces / $gaps);
}
push @words, q{ } x ($spaces % $gaps);
} else {
push @words, q{ } x $spaces;
}
say q{"}, (join q{}, @words), q{"};
Challenge 2:
Largest Substring
You are given a string.
Write a script to return the length of the largest substring between two equal characters excluding the two characters. Return -1 if there is no such substring.
Example 1
Input: $str = "aaaaa"
Output: 3
For character "a", we have substring "aaa".
Example 2
Input: $str = "abcdeba"
Output: 5
For character "a", we have substring "bcdeb".
Example 3
Input: $str = "abbc"
Output: 0
For character "b", we have substring "".
Example 4
Input: $str = "abcaacbc"
Output: 4
For character "a", we have substring "bca".
For character "b", we have substring "caac".
For character "c", we have substring "aacb".
Example 5
Input: $str = "laptop"
Output: 2
For character "p", we have substring "to".
Because this challenge wants the length of the largest substring, the first order
of business is to record the position of each character in $str.
my %positions;
for $str.comb.kv -> $index, $char {
%positions{$char}.push($index);
}
We assign storage for the maximum substring length and set it to -1 by default.
my $max = -1;
Now For each character that appears more than once...
for %positions.values -> @indices {
if @indices.elems > 1 {
...we calculate the distance between the first and last occurrences. We have to substract from the distance to exclude the two equal characters at each end.
my $distance = @indices[*-1] - @indices[0] - 1;
If the distance is greater than the current value of $max, it becomes the new
value of it.
if $distance > $max {
$max = $distance;
}
}
}
Finally, we just print $max. If we had failed to find an appropriate substring,
this would still equal -1.
say $max;
This time, the Perl translation doesn't require any extra code; everything can be done with core Perl itself.
my %positions;
Note here, to substitute for Raku's .kv(), I'm using indexed() which is now
a standard builtin in the most recent releases of Perl.
for my ($index, $char) (indexed split //, $str) {
push @{$positions{$char}}, $index;
}
my $max = -1;
for my $indices (values %positions) {
if (scalar @{$indices} > 1) {
my $distance = $indices->[-1] - $indices->[0] - 1;
if ($distance > $max) {
$max = $distance
}
}
}
say $max;