TL;DR

On we go with TASK #2 of Perl Weekly Challenge #082.

Do you really care for the solution to this task? Myoungjin Jeon in the comments made me realize that I’m solving a different problem here, so I’ll leave it but… I’ll solve it somewhere else. Sorry 🤭

# The challenge

You are given 3 strings; `\$A`, `\$B` and `\$C`. Write a script to check if `\$C` is created by interleave `\$A` and `\$B`. Print 1 if check is success otherwise 0.

# The questions

I guess this challenge has, so far, the vaguest definition that I found so far. What’s an interleave exactly? I guess something like take some from here, then some from there, then back to here, … qualifies, but how exactly?

So the questions in this case would be mainly aimed at understanding what the interleave is exactly; from the examples, it seems:

• 1 character from each character, interleave is not valid if there are two characters from the same string
• both `\$A` and `\$B` might be the first

Then, another question comes to mind: is the interleave to be meant at the character level or at the byte level? Whatever, we will just stick with what `substr` thinks that’s best, and leave that to the external program and how it feeds us with `\$A`, `\$B`, and `\$C`.

# The solution

We can first do some preliminary checks to see if there’s a chance that the three strings are actually compliant to the test. All of them can be carried out with just the strings’ lenghts:

``````my (\$lA, \$lB, \$lC) = map { length \$_ } (\$A, \$B, \$C);
(\$lA, \$lB, \$A, \$B) = (\$lB, \$lA, \$B, \$A) if \$lA > \$lB;
``````

We swap `\$A` and `\$B` to make the former shorter or at most as long as the latter, which will simplify our life in the lines after.

It’s now easy to rule out impossible situation, i.e.:

• lengths don’t add up in the correct way;
• either `\$A` or `\$B` are too longer than the other one.

This brings us to this:

``````return 0 if (\$lA + \$lB != \$lC) || (\$lB > \$lA + 1);
``````

The astute reader knows at this point that we are in a `sub`… we’ll get to this shortly.

Now we’re left to checking whether the two strings are actually interleaving into the third string, we’re doing this two characters at a time. We first establish whether `\$A` or `\$B` can go first, by a test on the lengths; thanks to the rearrangement we did in the beginning, we know that `\$B` can always go first at this point:

``````my (\$fA, \$fB) = (\$lA == \$lB, 1); # can go first?
``````

If we ever drop to both of these flag variables being false… the two strings are not interleaved. Here’s the loop:

``````for my \$i (0 .. \$lB - 1) {
my (\$cA, \$cB) = map { substr \$_, \$i, 1 } (\$A, \$B);
my \$sC = substr \$C, 2 * \$i, 2;
\$fA &&= (\$sC eq (\$cA . \$cB));
\$fB &&= (\$sC eq (\$cB . \$cA));
return 0 unless \$fA || \$fB;
}
``````

Simply put, we take two characters from `\$C` and one character from both `\$A` and `\$B`, combining them depending on whether the strings can go first or not.

If we manage to exit from the loop… then the two strings are interleaved:

``````return 1;
``````

Here’s the complete script if you want to play with it:

``````#!/usr/bin/env perl
use 5.024;
use warnings;
use English qw< -no_match_vars >;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;

sub is_interleaving (\$A, \$B, \$C) {
my (\$lA, \$lB, \$lC) = map { length \$_ } (\$A, \$B, \$C);
(\$lA, \$lB, \$A, \$B) = (\$lB, \$lA, \$B, \$A) if \$lA > \$lB;
return 0 if (\$lA + \$lB != \$lC) || (\$lB > \$lA + 1);
my (\$fA, \$fB) = (\$lA == \$lB, 1); # can go first?
for my \$i (0 .. \$lB - 1) {
my (\$cA, \$cB) = map { substr \$_, \$i, 1 } (\$A, \$B);
my \$sC = substr \$C, 2 * \$i, 2;
\$fA &&= (\$sC eq (\$cA . \$cB));
\$fB &&= (\$sC eq (\$cB . \$cA));
return 0 unless \$fA || \$fB;
}
return 1;
}

my \$A = shift || 'XXY';
my \$B = shift || 'XXZ';
my \$C = shift || 'XXXXZY';
say is_interleaving(\$A, \$B, \$C);
``````

Have a good one!