This is my response to The Weekly Challenge #349.
Input: $str = "textbook"
Output: 2
Breakdown: "t", "e", "x", "b", "oo", "k"
The longest substring with one unique character is "oo".
Example 2:
Input: $str = "aaaaa"
Output: 5
Example 3:
Input: $str = "hoorayyy"
Output: 3
Breakdown: "h", "oo", "r", "a", "yyy"
The longest substring with one unique character is "yyy".
Example 4:
Input: $str = "x"
Output: 1
Example 5:
Input: $str = "aabcccddeeffffghijjk"
Output: 4
Breakdown: "aa", "b", "ccc", "dd", "ee", "ffff", "g", "h", "i", "jj", "k"
The longest substring with one unique character is "ffff".
#! /usr/bin/env raku
unit sub MAIN ($str where $str.chars > 0, :v(:$verbose)); # [1]
sub grouped ($str, :$verbose) # [2]
{
my @parts = gather
{
my $curr;
my $count = 0;
for $str.comb -> $char
{
if $count && $curr ne $char
{
take $curr x $count;
$count = 0;
}
$curr = $char;
$count++;
}
take $curr x $count;
}
say ": Grouped '$str' as { @parts.raku }" if $verbose;
return @parts;
}
say grouped($str, :$verbose)>>.chars.max; # [3]
[1] Ensure a string with at least one character.
[2] The «grouped» procedure returns a list of substrings, where each substring contains the same characters. The procedure is the same as I used in «group-position», way back in week 318. See Reverse Postition with Raku for a discussion.
[3] Get the lenghts of the substrings (>>.chars) and print
the highest one (.max).
Running it:
$ ./power-string textbook
2
$ ./power-string aaaaa
5
$ ./power-string hoorayyy
3
$ ./power-string x
1
$ ./power-string aabcccddeeffffghijjk
4
Looking good.
With verbose mode:
$ ./power-string -v textbook
: Grouped 'textbook' as ["t", "e", "x", "t", "b", "oo", "k"]
2
$ ./power-string -v aaaaa
: Grouped 'aaaaa' as ["aaaaa"]
5
$ ./power-string -v hoorayyy
: Grouped 'hoorayyy' as ["h", "oo", "r", "a", "yyy"]
3
$ ./power-string -v x
: Grouped 'x' as ["x"]
1
$ ./power-string -v aabcccddeeffffghijjk
: Grouped 'aabcccddeeffffghijjk' as ["aa", "b", "ccc", "dd", "ee", "ffff", \
"g", "h", "i", "jj", "k"]
4
U (up), D (down),
L (left) and R (right).
Input: $path = "ULD"
Output: false
(-1,1) <- (0,1)
| ^
v |
(-1,0) (0,0)
Example 2:
Input: $path = "ULDR"
Output: true
(-1,1) <- (0,1)
| ^
v |
(-1,0) -> (0,0)
Example 3:
Input: $path = "UUURRRDDD"
Output: false
(0,3) -> (1,3) -> (2,3) -> (3,3)
^ |
| v
(0,2) (3,2)
^ |
| v
(0,1) (3,1)
^ |
| v
(0,0) (3,0)
Example 4:
Input: $path = "UURRRDDLLL"
Output: true
(0,2) -> (1,2) -> (2,2) -> (3,2)
^ |
| v
(0,1) (3,1)
^ |
| v
(0,0) <- (1,0) <- (1,1) <- (3,0)
Example 5:
Input: $path = "RRUULLDDRRUU"
Output: true
(0,2) <- (1,2) <- (2,2)
| ^
v |
(0,1) (2,1)
| ^
v |
(0,0) -> (1,0) -> (2,1)
Let us disregard the «at any point along the sequence» for a moment,
and just look for situations where we end up at (0,0).
#! /usr/bin/env raku
unit sub MAIN ($path where $path ~~ /^ <[UDLR]>+ $/; # [1]
my $bag = $path.comb.Bag; # [2]
say $bag<L> == $bag<R> && $bag<U> == $bag<D>; # [3]
[1] Ensure the directional letters only, with at least one of them.
[2] Turn the string into individual letters, and those into
a Bag, a hash like structure that counts the frequencies.
See docs.raku.org/routine/Bag for more information about Bag.
[3] Report success if the Left and Right, and the Up and Down moves cancel each other out.
Running it:
$ ./meeting-point ULD
False
$ ./meeting-point ULDR
True
$ ./meeting-point UUURRRDDD
False
$ ./meeting-point UURRRDDLLL
True
$ ./meeting-point RRUULLDDRRUU
False
Looking good, except for the last one.
So, let us un-disregard the «at any point along the sequence»:
File: meeting-point
#! /usr/bin/env raku
unit sub MAIN ($path where $path ~~ /^ <[UDLR]>+ $/, # [0]
:v(:$verbose));
my $x = 0; # [1]
my $y = 0; # [1a]
for $path.comb -> $move # [2]
{
given $move # [3]
{
when 'L' { $x--; } # [3L]
when 'R' { $x++; } # [3R]
when 'U' { $y--; } # [3U]
when 'D' { $y++; } # [3D]
}
say ": $move -> ($x, $y)" if $verbose;
last if $x == $y == 0; # [4]
}
say $x == $y == 0; # [5]
[1] The starting position.
[2] Iterate over the moves.
[3] Update the position according to the given
move; L, R, U or D.
Note the we do not need a default clause to catch
other values, as the regexp in [0] ensures legal moves only.
See docs.raku.org/syntax/default when for more information about given/when/default.
[4] Skip the rest of the moves if we have reached (0,0). Note
that this check is done after the first move, so will not be
triggered initially.
[5] Report success if we are at (0,0), and failure otherwise.
Running it with verbose mode:
$ ./meeting-point -v ULD
: U -> (0, -1)
: L -> (-1, -1)
: D -> (-1, 0)
False
$ ./meeting-point -v ULDR
: U -> (0, -1)
: L -> (-1, -1)
: D -> (-1, 0)
: R -> (0, 0)
True
$ ./meeting-point -v UUURRRDDD
: U -> (0, -1)
: U -> (0, -2)
: U -> (0, -3)
: R -> (1, -3)
: R -> (2, -3)
: R -> (3, -3)
: D -> (3, -2)
: D -> (3, -1)
: D -> (3, 0)
False
$ ./meeting-point -v UURRRDDLLL
: U -> (0, -1)
: U -> (0, -2)
: R -> (1, -2)
: R -> (2, -2)
: R -> (3, -2)
: D -> (3, -1)
: D -> (3, 0)
: L -> (2, 0)
: L -> (1, 0)
: L -> (0, 0)
True
$ ./meeting-point -v RRUULLDDRRUU
: R -> (1, 0)
: R -> (2, 0)
: U -> (2, -1)
: U -> (2, -2)
: L -> (1, -2)
: L -> (0, -2)
: D -> (0, -1)
: D -> (0, 0)
True
And that's it.