Perl Weekly Challenge: Week 349

Challenge 1:

Power String

You are given a string.

Write a script to return the power of the given string.

The power of the string is the maximum length of a non-empty substring that contains only one unique character.

Example 1
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".

First we declare a variable to hold the maximum power of a string segment which is the power of the string as a whole. It is initialized to 0.

my $maxPower = 0;

Using a regular expression in the ,match() method, we split up $str into substrings each containing one particular character only. We use a for-loop to go through the list of substrings one at a time, assigning the current substring to $match.

for $str.match(/(.)$0*/, :g) -> $match {

We measure the power (i.e. the length) of $match with .chars() and if it is greater than the current value of $maxPower, it becomes the new value of $maxPower.

    my $power = $match.chars;
    if $power > $maxPower {
        $maxPower = $power;
    }
}

Finally, we output the value of $maxPower.

say $maxPower;

(Full code on Github.)

The Perl version works in exactly the same way as Raku.

my $maxPower = 0;

while ($str =~ /(.)\1*/g) {

The $& variable (I use the English module to give it the more legible name $MATCH) contains the most recent regular expression match. In earlier versions of Perl it was not recommended to use because of performance reasons and you were supposed to use workarounds instead but in modern versions of Perl those issues have been solved.

    my $power = length $MATCH;
    if ($power > $maxPower) {
        $maxPower = $power;
    }
}

say $maxPower;

(Full code on Github.)

Challenge 2:

Meeting Point

You are given instruction string made up of U (up), D (down), L (left) and R (right).

Write a script to return true if following the instruction, you meet (0,0) at any point along the sequence.

Example 1
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)

The MAIN() function is extremely simple—just one line in fact.

say follow($path);

The work is all done in the follow() function.

sub follow($path) {

It is not explicitly stated but the diagrams in the spec reveal that the coordinate system has an origin at 0, 0. The first number represents locations on the horizontal axis (i.e columns) and the second represents locations on the vertical axis (i.e. rows.) Columns to the left and rows to the bottom are "lower" than columns to the right or rows to the top. Based on this information, we start by defining two variables to hold the current column and row.

    my $col = 0;
    my $row = 0;

We split $path into individual characters and for each of these characters...

    for $path.comb -> $direction {
        given $direction {

...we incremwnt or decrement row or column as needed.

            when 'U' {
                $row--;
            }
            when 'D' {
                $row++;
            }
            when 'L' {
                $col--;
            }
            when 'R' {
                $col++
            }

Although the spec guarantees the input will only contain valid characters, I've included a default that catches all other character but does nothing just to be on the safe side.

            default {
            }
        }

We then check if we are at the origin. If so, we stop processing and return True.

        if $col == 0 && $row == 0 {
            return True;
        }
    }

If we have processed every character in $path and not crossed the origin, we return False.

    return False;
}

(Full code on Github.)

The Perl version also simply calls follow() and prints true or false depending on its' return value.

say 0+(follow $path) ? 'true' : 'false';

This is follow().

sub follow($path) {
    my $col = 0;
    my $row = 0;

Perl also has given/when construct but it has been deprecated. Instead we use for and if to implement a switch.

    for my $direction (split //, $path) {
        for ($direction) {
            if (/U/) { $row-- }
            if (/D/) { $row++ }
            if (/L/) { $col-- }
            if (/R/) { $col++ }
        }

        if ($col == 0 && $row == 0) {
            return true;
        }
    }

    return false;
}

(Full code on Github.)