没有反转/反转的lexigraphic顺序的字符串排列

时间:2017-01-30 00:57:06

标签: string algorithm perl permutation combinatorics

问题:

我想在lexigraphical中生成一个字符串排列列表,但不包括字符串反转。例如,如果我有以下字符串:abc,我想生成以下列表

abc
acb
bac

而不是典型的

abc
acb
bac
bca
cab
cba

另一个例子看起来像这样:

100
010

而不是

100
010 
001

目前,我可以使用perl生成排列,但我不确定如何最好地删除反向重复项。

我曾想过应用以下内容:

create map with the following:
1)   100
2)   010
3)   001
then perform the reversion/inversion on each element in the map and create a new map with:

 1') 001
 2') 010
 3') 100

然后进行比较,如果引用的列表值与原始值匹配,则将其保留在原位,如果它不同,如果它的索引大于中位数索引,则保留它,否则删除。

麻烦的是,我不确定这是否是一种有效的方法。

任何建议都会很棒。

2 个答案:

答案 0 :(得分:0)

建议的重复command substitution中的答案并不处理重复的元素(因为那不是该问题的一部分),所以天真地跟随它会包括例如0100和0010.所以这不是完全重复的。但这个想法适用。

生成所有排列,但只对具有$_ le reverse $_的排名进行过滤。我认为这基本上就是你在问题中的建议,但当应用于每个排列的简单表达式告诉你是否包含它时,不需要计算地图。

答案 1 :(得分:0)

示例表示的两种可能性是所有元素不同的排列(abcd),或两个符号的变体,其中一个恰好出现一次(1000)。更一般的案例也得到了解决。

非重复元素(排列)

在这里,我们可以使用Algorithm::Permute,以及特定观察: 第一个元素大于最后一个元素的每个排列都需要排除。它来自this post,在answer by ysth中出现。

此规则如下所示。考虑没有第一个和最后一个元素的字符串的子字符串。对于每个这样的子字符串,字符串的所有排列必须包含其反转。其中一个用最后一个和第一个填充,因此是字符串的反转。通过构造,对于每个子串,恰好有一个逆。因此,需要排除每个字符串交换的第一个和最后一个元素的排列。

use warnings;
use strict;
use feature 'say';

use Algorithm::Permute;

my $size = shift || 4;
my @arr = ('a'..'z')[0..$size-1];  # 1..$size for numbers

my @res;    
Algorithm::Permute::permute { 
    push @res, (join '', @arr) unless $arr[0] gt $arr[-1] 
} @arr; 

say for @arr;

带有重复元素 abbcd)的问题可以用与上面完全相同的方式处理,我们还需要修剪重复项作为b生成{{1}的排列}和abbcd(相同)

abbcd

在施工期间这样做不会降低复杂性,也不会加快速度。

到目前为止,use List::MoreUtils 'uniq'; # build @res the same way as above ... my @res = uniq @res; 被引用为模块中最快的方法。它比我测试的其他模块(下图)快一个数量级,在我的系统上为10个元素花费大约1秒。但请注意,此问题的复杂性在于 factorial 的大小。它爆炸得非常快。

两个符号,其中一个符号恰好出现一次(变体)

这是不同的,上述模块不适用于此,排除标准也不起作用。还有其他模块,最后见。但是,这里的问题很简单。

从列表中的permute和'walk'(1,0,0,...)开始,一直到“midpoint” - 这是偶数列表的一半(4-for 8-long),或者是下一半奇数尺寸(5长9长)。以这种方式获得的所有字符串,通过将1移动一个位置直到中点,形成集合。第二个“半”是他们的反转。

1

我们需要在最后一个索引之前停止,因为它已在上一次迭代中填充。

如果两个符号(use warnings; use strict; my $size = shift || 4; my @n = (1, map { 0 } 1..$size-1); my @res = (join '', @n); # first element of the result my $end_idx = ( @n % 2 == 0 ) ? @n/2 - 1 : int(@n/2); foreach my $i (0..$end_idx-1) # stop one short as we write one past $i { @n[$i, $i+1] = (0, 1); # move 1 by one position from where it is push @res, join '', @n; } print "$_\n" for @res; )可能重复出现,则可以修改此项,但使用模块然后排除反转更简单。 Algorithm::Combinatorics具有满足所有需求的例程。对于长度0,1的{​​{1}}和0的所有变体,两者都可以重复

1

然后可以通过强力搜索排除反向元素,最坏情况下 O(N 2 复杂度。

另请注意Math::Combinatorics