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!

Comments? Octodon, , GitHub, Reddit, or drop me a line!