So I have an array my @a = (a,b,c,d,e,f)
my @b开发者_运维问答 = (c,d,e)
I want to find if there are three consecutive elements in @a
that match those in @b
.
Then, if there are, I want to get the indices those elements live in.
So in the case above I want to get an array such as (2,3,4)
.
Another example:
my @a = (1,2,3,4,5)
my @b = (2,3)
Output: (1,2)
A naive approach:
@A = 1..5;
@B = 2..3;
A_LOOP:
for my $a_index (0..$#A) {
for my $b_index (0..$#B) {
next A_LOOP unless $A[$a_index+$b_index] eq $B[$b_index];
}
@results = map $a_index+$_, 0..$#B;
last;
}
If this isn't fast enough (unlikely, given your examples), Boyer-Moore isn't that difficult to implement.
Here is a general solution. This uses the 'all' function, from List::MoreUtils to reduce the comparison to a true/false result, which simplifies the logic a little.
I put it into the form of a sub to which you pass a reference to any two arrays. The first array ref passed to the function should be the superset, and the second array ref should refer to the subset array.
What I like about this solution is that it can apply to any two simple arrays (it isn't constrained to seeking a two-element subset, for example). I did choose a string comparison of the elements (eq) as opposed to numeric (==). That way it works if you have non-numeric elements. However, it will evaluate '00' and '0' as non-equal (because they're not the same string). If you favor a numeric comparison, just find the 'eq' and change it to '=='.
Here's the code:
use 5.010_001;
use strict;
use warnings;
use List::MoreUtils qw/all/;
my @array_a = qw/1 2 3 4 5/;
my @array_b = qw/2 3/;
{
local $, = " ";
my( @results ) = find_group( \@array_a, \@array_b );
say "Success at ", @results if @results;
}
sub find_group {
my( $array_1, $array_2 ) = @_;
foreach my $array_1_idx ( 0 .. $#{$array_1} ) {
my $moving_idx = $array_1_idx;
return $array_1_idx .. ( $moving_idx - 1 ) if
all { $_ eq $array_1->[$moving_idx++] } @{$array_2};
}
return ();
}
精彩评论