site.base_url }}/">The Bear's Den -->

The Bear's Den

Enter at your own risk

Semi-Permutational Contiguities

Task 1: Contiguous Array

Submitted by: Mohammad Sajid Anwar


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

Solution

Though the task states

an array of binary numbers

I’ll assume the array contains only zeroes and ones as elements, not binary numbers like 10011.

This task can be solved in \(\mathcal{O}(N)\) operations.

We may turn all zeroes in the array @binary to -1, resulting in a new array @z. If a contiguous subarray of @binary contains an equal number of zeroes and ones, then the same subarray of @z has a sum of zero and vice versa.

Building an array @s as the cumulative sums over @z:

$s[0] = 0
$s[$i] = $s[$i - 1] + $z[$i - 1]

The sum over the subarray @z[$p .. $q - 1] (having a length of $q - $p) equals $s[$q] - $s[$p]. A subarray having a sum of zero thus can be identified by the positions of equal elements within @s.

In a hash %firstlast we now record the indices of the first and last appearance for every element of @s. Then we take the maximum difference over the (first, last) pairs as the requested result.

Though the arrays @z and @s are helpful when describing the procedure, they are actually not needed. All the given steps may be performed within a single loop without storing the values in @z and @s. The last index doesn’t need to be stored, neither.

This may be implemented as:

use strict;
use warnings;

sub max_contig {
    my %first = (0 => 0);
    my ($max, $s, $i) = 0;
    for my $b (@_) {
        $s += $b || -1;
        $i++;
        $first{$s} = $i, next unless exists $first{$s};
        my $len = $i - $first{$s};
        $max = $len if $len > $max;
    }
    $max;
}

See the full solution to task 1.

Task 2: Semi-Ordered Permutation

Submitted by: Mohammad Sajid Anwar


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.

Solution

Let the index of 1 be $min and the index of n be $max. We need $min swaps to move 1 to position 0 and we need n - 1 - $max swaps to move n to position n - 1. In case $max is smaller than $min, we save one swap when exchanging 1 with n.

Using PDL we may find $min and $max simultaneously with a single call to minmaximum.

use strict;
use warnings;
use PDL;

sub semi_ordered {
    my (undef, undef, $min, $max) = minmaximum long @_;

    $min + $#_ - $max - ($max < $min);
}

See the full solution to task 2.


If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.