Perl-2个或更多字符串的最长公共前缀?

Perl-2个或更多字符串的最长公共前缀?,perl,Perl,如何创建一个Perl子例程,它将接受一个数组,并为其2个或更多元素找到最长的公共前缀?(字符串) 我有以下代码: sub longest_common_prefix { $prefix = shift; for (@_) { chop $prefix while (! /^\Q$prefix\E/); } return $prefix; } 但只有在查找all字符串中最长的公共前缀时,它才起作用 例如,如果我传递具有以下字符串的数组: a

如何创建一个Perl子例程,它将接受一个数组,并为其2个或更多元素找到最长的公共前缀?(字符串)

我有以下代码:

sub longest_common_prefix {
    $prefix = shift;
    for (@_) {
        chop $prefix while (! /^\Q$prefix\E/);
        }
    return $prefix;
}
但只有在查找all字符串中最长的公共前缀时,它才起作用

例如,如果我传递具有以下字符串的数组:

aaaBGFB
aaaJJJJ
jjfkBBB
aaaHGHG
我希望它返回
aaa
作为答案


谢谢

一种方法是将信息存储在散列中。在本例中,我将哈希键设置为每个前缀的长度,该值为找到的实际前缀

请注意,如果存在相同长度的前缀,此方法将覆盖键和值,因此您将始终获得找到的最长长度的最后一个前缀(
sort()
负责查找最长的前缀)

正则表达式说“找到字符串中的第一个字符并捕获它,然后使用在第二个捕获中找到的字符,并捕获尽可能多的字符”。然后将该字符串
join()
转换为标量并放入哈希

use warnings;
use strict;

my %prefixes;

while (<DATA>){
    my $prefix = join '', /^(.)(\1+)/;
    $prefixes{length $prefix} = $prefix; 
}

my $longest = (sort {$b <=> $a} keys %prefixes)[0];
print "$prefixes{$longest}\n";

__DATA__
aaBGFB
aaaJJJJ
jjfkBBB
aaaHGHG

一种方法是将信息存储在散列中。在本例中,我将哈希键设置为每个前缀的长度,该值为找到的实际前缀

请注意,如果存在相同长度的前缀,此方法将覆盖键和值,因此您将始终获得找到的最长长度的最后一个前缀(
sort()
负责查找最长的前缀)

正则表达式说“找到字符串中的第一个字符并捕获它,然后使用在第二个捕获中找到的字符,并捕获尽可能多的字符”。然后将该字符串
join()
转换为标量并放入哈希

use warnings;
use strict;

my %prefixes;

while (<DATA>){
    my $prefix = join '', /^(.)(\1+)/;
    $prefixes{length $prefix} = $prefix; 
}

my $longest = (sort {$b <=> $a} keys %prefixes)[0];
print "$prefixes{$longest}\n";

__DATA__
aaBGFB
aaaJJJJ
jjfkBBB
aaaHGHG
我会用修改过的

通常,可以使用以下内容添加到trie中:

sub add {
    my $p = \shift;
    my $s = shift;
    $p = \( $$p->{$_} ) for split(//, $s);
    $$p->{''} = 1;
}
但我们需要两个修改:

  • 添加字符串时,必须添加字符串的所有前缀。例如,添加
    abc
    也应将
    a
    ab
    添加到trie中
  • 当添加到trie时,我们希望返回以前存在的路径部分的长度
因此,我们需要:

sub add {
    my $p = \shift;
    my $s = shift;

    my $cp_len = 0;
    for (split(//, $s)) {
       $p = \( $$p->{$_} );
       ++$cp_len if $$p->{$_}{''};
       $$p->{''} = 1;
    }

    return $cp_len;
}
将(优化版)此功能与查找列表中最长字符串的算法以及从列表中删除重复字符串的算法相结合,以获得以下解决方案:

use strict;
use warnings;
use feature qw( say );

sub add {
    my $p = \shift;
    my $s = shift;

    my $cp_len = 0;
    for (split(//, $s)) {
       ++$cp_len if exists($$p->{$_});
       $p = \( $$p->{$_} );
    }

    return $cp_len;
}

my $t;
my $lcp_len = 0;  # lcp = longest common prefix
my %lcps;
while (<>) {
   chomp;
   my $cp_len = add($t, $_)
      or next;

   if ($cp_len >= $lcp_len) {
      if ($cp_len > $lcp_len) {
         $lcp_len = $cp_len;
         %lcps = ();
      }

      $lcps{ substr($_, 0, $cp_len) } = 1;
   }
}

my @lcps = sort keys %lcps;

if (@lcps) {
   say "Longest common prefix(es): @lcps";
} else {
   say "No common prefix";
}
输出:

aaa
Longest common prefix(es): hijkl mnopq
上面所用的时间与输入字符的数量成正比。

我会使用修改过的

通常,可以使用以下内容添加到trie中:

sub add {
    my $p = \shift;
    my $s = shift;
    $p = \( $$p->{$_} ) for split(//, $s);
    $$p->{''} = 1;
}
但我们需要两个修改:

  • 添加字符串时,必须添加字符串的所有前缀。例如,添加
    abc
    也应将
    a
    ab
    添加到trie中
  • 当添加到trie时,我们希望返回以前存在的路径部分的长度
因此,我们需要:

sub add {
    my $p = \shift;
    my $s = shift;

    my $cp_len = 0;
    for (split(//, $s)) {
       $p = \( $$p->{$_} );
       ++$cp_len if $$p->{$_}{''};
       $$p->{''} = 1;
    }

    return $cp_len;
}
将(优化版)此功能与查找列表中最长字符串的算法以及从列表中删除重复字符串的算法相结合,以获得以下解决方案:

use strict;
use warnings;
use feature qw( say );

sub add {
    my $p = \shift;
    my $s = shift;

    my $cp_len = 0;
    for (split(//, $s)) {
       ++$cp_len if exists($$p->{$_});
       $p = \( $$p->{$_} );
    }

    return $cp_len;
}

my $t;
my $lcp_len = 0;  # lcp = longest common prefix
my %lcps;
while (<>) {
   chomp;
   my $cp_len = add($t, $_)
      or next;

   if ($cp_len >= $lcp_len) {
      if ($cp_len > $lcp_len) {
         $lcp_len = $cp_len;
         %lcps = ();
      }

      $lcps{ substr($_, 0, $cp_len) } = 1;
   }
}

my @lcps = sort keys %lcps;

if (@lcps) {
   say "Longest common prefix(es): @lcps";
} else {
   say "No common prefix";
}
输出:

aaa
Longest common prefix(es): hijkl mnopq

上述操作所花费的时间与输入字符的数量成正比。

您可以保留由第一个字符键入的单词数组的散列。根据定义,如果有以相同字母开头的单词,则这些单词至少共享该字母的一个字符公共前缀。然后通过按字符单步遍历哈希,将前缀减少为单个最长前缀:

use strict; use warnings;
sub lcp {
    (join("\0", @_) =~ /^ ([^\0]*) [^\0]* (?:\0 \1 [^\0]*)* $/sx)[0];
}

my %HoA;
my $longest='';

while (my $line=<DATA>){
    $line =~ s/^\s+|\s+$//g ;
    push @{ $HoA{substr $line, 0, 1} }, $line if $line=~/^[a-zA-Z]/;
}

for my $key ( sort (keys %HoA )) {
    if (scalar @{ $HoA{$key} } > 1){
        my $lon=lcp(@{ $HoA{$key} });
        my $s = join ', ', map { qq/"$_"/ } @{ $HoA{$key} };
        print "lcp: \"$lon\" for ($s)\n";
        if (length($lon) > length($longest)) {
            $longest=$lon;
        }            
    }
    else{
        print "$key: no common prefix\n";
    }
}
print "\nlongest common prefix is \"$longest\"\n";

__DATA__
aardvark
aaaBGFB
aaaJJJJ
jjfkBBB
aaaHGHG
interspecies
interstellar
interstate   

您可以保留由第一个字符键入的单词数组的散列。根据定义,如果有以相同字母开头的单词,则这些单词至少共享该字母的一个字符公共前缀。然后通过按字符单步遍历哈希,将前缀减少为单个最长前缀:

use strict; use warnings;
sub lcp {
    (join("\0", @_) =~ /^ ([^\0]*) [^\0]* (?:\0 \1 [^\0]*)* $/sx)[0];
}

my %HoA;
my $longest='';

while (my $line=<DATA>){
    $line =~ s/^\s+|\s+$//g ;
    push @{ $HoA{substr $line, 0, 1} }, $line if $line=~/^[a-zA-Z]/;
}

for my $key ( sort (keys %HoA )) {
    if (scalar @{ $HoA{$key} } > 1){
        my $lon=lcp(@{ $HoA{$key} });
        my $s = join ', ', map { qq/"$_"/ } @{ $HoA{$key} };
        print "lcp: \"$lon\" for ($s)\n";
        if (length($lon) > length($longest)) {
            $longest=$lon;
        }            
    }
    else{
        print "$key: no common prefix\n";
    }
}
print "\nlongest common prefix is \"$longest\"\n";

__DATA__
aardvark
aaaBGFB
aaaJJJJ
jjfkBBB
aaaHGHG
interspecies
interstellar
interstate   

如果混音中有“j12345”,会发生什么情况?如果我们加上“BB1”和“BB2”怎么办?它是在寻找任意两个字符串之间最长的前缀吗?或者它是至少有1个共同点的字符串的LCP?后者,是的,如果不清楚,很抱歉。它可能是任何东西如果“j12345”在混合中会发生什么?如果我们加上“BB1”和“BB2”怎么办?它是在寻找任意两个字符串之间最长的前缀吗?或者它是至少有1个共同点的字符串的LCP?后者,是的,如果不清楚,很抱歉。对于
qw(abcd abce xxy xxz)
,它可能是任何失败的东西。(打印
xx
而不是
abc
)这仅适用于运行相同字母的前缀。例如,如果你有
(aaBGFB,aaaJJJJ,种间,星际)
,最长的通用前缀是
inter
对于
qw(abcd abce xxy xxz)
。(打印
xx
而不是
abc
)这仅适用于运行相同字母的前缀。例如,如果你有
(aaBGFB,aaajjj,种间,星际)
,最长的公共前缀是
inter