| 08 May 2026 | Challenge 372 |
Regular Sub-Gaps
Task 1: Rearrange Spaces
Submitted by: Mohammad Sajid Anwar
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.
Solution
Two basic cases can be differentiated:
- There are two or more words: the available spaces are distributed uniformly over the gaps as uniform inter-word space and the remainder is appended as remainig trailing space.
- There is zero or one word: the available spaces are used as remainig trailing space completely.
Perl
Use split to extract the words into @words and tr to extract all spaces into $sp.
Then use the regex engine to distribute the spaces.
There is a “happy case” with \(n \gt 1\) words resulting in one or more gaps.
With \(r = n - 2\) the spaces in $sp can be distributed with this regex:
$sp =~ /^( +)\1{$r}( *)$/
It selects and captures one or more spaces, tries to match the captured string $r more times and captures all remaining spaces in a second capture group. As + operates greedy,
$1 will contain the uniform inter-word space and $2 the remaining trailing space after a successful match.
This is problematic in the general case in two aspects:
$rwill become negative when there are less than two words, leading to an illegal regex. This may be fixes by setting negative values to zero.$1will consume all spaces when there are less than two words. The fix for this issue is a bit more complex. The sub-regex( +)\1{$r}can be made conditional on \(n \gt 1\) as:(?(?{$n > 1})( +)\1{$r})
With
$sp =~ /^(?(?{$n > 1})( +)\1{$r})( *)$/
and \(n > 1\) it acts as before, whereas for
\(n \le 1\), $1 will be undef and $2 will contain all spaces.
The last regex can be used in a substitute operation with an evaluated right operand to produce the result as:
join($1, @words) . $2
With less than two words, actually there is nothing to join.
However, $1 is still used as an argument to join and is undef.
The emitted warning needs to be suppressed.
use strict;
use warnings;
use experimental 'signatures';
sub rearrange_spaces ($str) {
my @words = split ' ', $str;
my $rep = @words > 1 ? @words - 2: 0;
no warnings 'uninitialized';
$str =~ tr/ //cdr =~ s{^(?(?{@words > 1})( +)\1{$rep})( *)$}
{join($1, @words) . $2}er;
}
See the full solution to task 1.
J
Naming some supplementary verbs to make the solution better readable.
spaces ycounts the number of spaces in the stringyspaces =. +/@:(' '&=)gaps ygives one less than the number of items iny. This is the number of gaps if there is at least one item inygaps =. <:@#The result is
_1for an empty list but this does not harm as the case “less than one item” needs to be treated specially anyway.x divmod yforx > 0calculates the integer quotient and the remainder \((\lfloor \frac{y}{x} \rfloor, y \operatorname{mod} x)\). Otherwise it returns \((0, y)\).divmod =. ((0: , ])`(<.@%~ , |)@.(0&<@[))spbx ycreates a box ofyspacesspbx =. <@(#&' ')x insert yacts on two boxed character arraysxandyby inserting the content ofy’s first box between the items ofxand appending the content ofy’s last box at the end. The result is unboxed.insert =. >@{:@] ,~ >@{.@] joinstring [
These verbs are assembled to the resulting verb rearrange:
- split the given string at spaces into a list of boxed words using
cutopenand use the result as the left argument of the following verb train:- count the spaces
- count the gaps
- apply
divmodbetween the previous results - create uniform inter-word-space and the remaining trailing space from the previous result
- insert the spaces in the word list from the first step
- fix supplementary verbs using
f., i.e. replace the names by their values.
rearrange =: cutopen ([ insert [: spbx"0 gaps@[ divmod spaces@]) f. ]
rearrange&.> ' challenge ';'coding is fun';'a b c d';' team pwc ';' the weekly challenge '
┌─────────────┬───────────────┬────────┬─────────────────┬───────────────────────────┐
│challenge │coding is fun│a b c d │team pwc│the weekly challenge │
└─────────────┴───────────────┴────────┴─────────────────┴───────────────────────────┘
See the full solution.
Task 2: Largest Substring
Submitted by: Mohammad Sajid Anwar
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".
Solution
Perl
In the regular expression qr{(.)(.*)\1} the second capture group matches any substring enclosed within two instances of the same character.
Recording the maximum length of all such substrings by forcing the match to fail after each substring match reveals the solution to this task.
use strict;
use warnings;
use List::Util 'max';
sub largest_substring {
my $l = -1;
shift =~ /(.)(.*)\1(?{$l = max $l, length $2})(*FAIL)/;
$l;
}
See the full solution to task 2.
J
Following a different approach in J:
- identify the indices of the first and last appearance of every unique character from the string
- find the maximum difference
- decrement by one
largest_substring =: ] ([: <: [: >./ i: - i.) ~.
largest_substring&.> 'aaaaa';'abcdeba';'abbc';'abcaacbc';'laptop'
┌─┬─┬─┬─┬─┐
│3│5│0│4│2│
└─┴─┴─┴─┴─┘
See the full solution.