Perl Weekly Challenge: Week 297

Challenge 1:

Contiguous Array

You are given an array of binary numbers, @binary.

Write a script to return the maximum length of a contiguous subarray with an equal number of 0 and 1.

Example 1
Input: @binary = (1, 0)
Output: 2

(1, 0) is the longest contiguous subarray with an equal number of 0 and 1.
Example 2
Input: @binary = (0, 1, 0)
Output: 2

(1, 0) or (0, 1) is the longest contiguous subarray with an equal number of 0 and 1.
Example 3
Input: @binary = (0, 0, 0, 0, 0)
Output: 0
Example 4
Input: @binary = (0, 1, 0, 0, 1, 0)
Output: 4

I had hoped to be able to do this entirely with one regular expression but try as I might, I was not able to get it to work. This is what I came up with instead.

A variable is declared to store the longest length; initially this is 0.

my $longest = 0;

While we have two or more elements (obviously we cannot have equal 1s and 0s with only one element)...

while @ints.elems > 1 {

...we .join() the elements of @ints to form a string and look in it for the longest sequence of 10 or 01 using a regular expression and assign the length (determined with .chars()) to the variable $length. If there is no sequence, $length = 0.

    my $length = (@ints.join.match(/([01|10]+)/, :g))[0] ?? $0.chars !! 0;

If $length is larger than the current value of $longest it becomes the new value of $longest.

    if $length > $longest {
        $longest = $length;
    }

@ints is shrunk by removing the leftmost element with shift() and the loop is run again.

    shift @ints;
}

Finally, we output $longest.

say $longest;

(Full code on Github.)

And this is the Perl version which works the same way.

my $longest = 0;

while (scalar @ints > 1) {
    (join q{}, @ints) =~ /((?:01|10)+)/g;
    my $length = length($1) // 0;

    if ($length > $longest) {
        $longest = $length;
    }

    shift @ints;
}

say $longest;

(Full code on Github.)

Challenge 2:

Semi-Ordered Permutation

You are given permutation of $n integers, @ints.

Write a script to find the minimum number of swaps needed to make the @ints a semi-ordered permutation.

A permutation is a sequence of integers from 1 to n of length n containing each number exactly once. A permutation is called semi-ordered if the first number is 1 and the last number equals n.

You are ONLY allowed to pick adjacent elements and swap them.

Example 1
Input: @ints = (2, 1, 4, 3)
Output: 2

Swap 2 <=> 1 => (1, 2, 4, 3)
Swap 4 <=> 3 => (1, 2, 3, 4)
Example 2
Input: @ints = (2, 4, 1, 3)
Output: 3

Swap 4 <=> 1 => (2, 1, 4, 3)
Swap 2 <=> 1 => (1, 2, 4, 3)
Swap 4 <=> 3 => (1, 2, 3, 4)
Example 3
Input: @ints = (1, 3, 2, 4, 5)
Output: 0

Already a semi-ordered permutation.

First we reserve storage for a count of how many swaps are made.

my $swaps = 0;

Then until the first element of @ints is 1 and the last element is equal to the number of elements in the array...

while @ints[0] != 1 && @ints[*-1] != @ints.elems {

...we go through all the elements of @ints except the first one.

    for 1 .. @ints.end -> $i {

We compare the current element to the one before it and if the one before is larger...

        if @ints[$i - 1] > @ints[$i] {

We swap them. I was surprised that Raku doesn't have a standard swap method as it has practically everything else.

            my $temp = @ints[$i];
            @ints[$i] = @ints[$i - 1];
            @ints[$i - 1] = $temp;

We add 1 to the number of swaps made.

            $swaps++;
        }
    }

These steps are repeated until we achieve a semi-ordered permutation.

}

Then we say() the number of swaps made.

say $swaps;

(Full code on Github.)

The Perl version is a direct translation from Raku.

my $swaps = 0;

while ($ints[0] != 1 && $#ints != scalar @ints) {
    for my $i (1 .. scalar @ints - 1) {
        if ($ints[$i - 1] > $ints[$i]) {
            my $temp = $ints[$i];
            $ints[$i] = $ints[$i - 1];
            $ints[$i - 1] = $temp;
            $swaps++;
        }
    }
}

say $swaps;

(Full code on Github.)