如何生成受限替换的排列?

How to generate permutations of restricted substutions?

最好用一个例子来解释。

假设你有一个替换列表,将使用 PHP 因为那是我的目标:

$s = array(
    'p' => array('b', 'f', 'v'),
    'a' => array('e'),
    't' => array('d', '')
);

上面的意思是'p'可以用'b'、'f'、'v'代替; 'a' 来自 'e';和 't' 通过 'd' 或什么都没有。每个列表一次只能替换一个。

因此生成 'pa' 的所有替换将得到:'ba'、'fa'、'va'、'pe'、'be'、'fe', 've'

并生成 'papa' 的所有替换:'baba'、'fafa'、'vava'、'pepe' 'bebe'、'fefe' , 'veve'

我可以很容易地排列上面的元素:

// 2 ^ count($s) permutations, assuming count($s) < 31
$k = 1 << count($s);
for ($l = 0; $l < $k; $l++)
{
    $x = $input;
    for ($m = $l, reset($s); $m; $m >>= 1, next($s))
        if ($m & 1)
            // Will fail here, maybe this should be an outer loop but how?
            // For now, just replacing with the first element
            $x = str_replace(key($s), current($s)[0], $x);
    print $x."\n";
}

只是无法理解如何正确地进行内部替换。

我考虑过将 $s 转换为一系列简单的替换:

$t = array(
    array('p' => 'b'),
    array('a' => 'e'),
    array('p' => 'b', 'a' => 'e'),
    array('p' => 'f'),
    ...

但这仍然让我回到同样的问题。

如有任何建议,我们将不胜感激。

您对 for 循环与数组指针的管理过于复杂。

以下是一种非常幼稚的方法,可以通过采用递归等其他策略来简化。

function generate_permutations($subtitutions, $subject) {
    $permutations = array($subject);

    foreach ($subtitutions as $search => $replacements) {
        $new_permutations = array();

        foreach ($replacements as $replacement) {
            foreach ($permutations as $permutation) {
                if (strpos($permutation, $search) === false) {
                    continue;
                }

                $new_permutations[] = str_replace($search, $replacement, $permutation);
            }
        }

        $permutations = array_merge($permutations, $new_permutations);
    }

    return $permutations;
}

注意:我只用你的例子测试过。

以 Jason McCreary 的回答为灵感,我找到了一个使用最少内存的解决方案。数组$X最大可以得到的是Π(|排列列表|),所以对于下面的例子3×1×2 = 6.

$input = 'pat';

$S = array(
    'p' => array('b', 'f', 'v'),
    'a' => array('e'),
    't' => array('d', '')
);

// 2 ^ count($S) permutations, assuming count($S) < 31
for ($k = 1 << count($S), $l = 0; $l < $k; $l++)
{
    $X = array($input);
    for ($m = $l, reset($S); $m; $m >>= 1, next($S))
        if ($m & 1)
        {
            $w = key($S);
            $Y = $X;
            $X = array();
            foreach ($Y as $y)
                foreach (current($S) as $s)
                    $X[] = str_replace($w, $s, $y);
        }

    foreach ($X as $x)
        print $x.' ';
}
print "\n";

输出:

pat bat fat vat pet bet fet vet pad pa bad ba fad fa vad va ped pe bed be fed fe ved ve