如何有效地生成给定长度字母的所有可能字母组合的数组?

时间:2014-08-01 16:51:16

标签: php arrays algorithm recursion

想象一下,我有一袋26个拼字游戏瓷砖 - 英文字母中每个字母一个。

我的目标是创建一个包含最长n个字母的所有可能字符串的数组。说n=3

约束:

  1. 字母必须始终按字母顺序排列(ABC,而不是CBA;组合,而不是排列)
  2. 字符串必须是< = n个字母长(允许算法突破给定长度的任何循环)
  3. 信件不得重复(A,而不是AA
  4. 如何在PHP中最有效地生成此数组?

    换句话说,我如何避免强制循环所有可能的组合并过滤掉那些与上述规则不符的内容?


    如果我的字母只包含3个字母 - $alphabet = range('a','c'); - 我希望输出7个项目(3C1 + 3C2 + 3C3)的数组:[A,B,C,AB,AC,BC,ABC]

    如果我的字母只包含4个字母 - $alphabet = range('a','d'); - 我希望输出15个项目(4C1 + 4C2 + 4C3 + 4C4)的数组:[A,B,C,D,AB,AC,AD,BC,BD,CD,ABC,ABD,ACD,BCD,ABCD]。但是如果我想限制只用<= 3个字符串长,那么我会忽略ABCD只产生14个项目(4C1 + 4C2 + 4C3)。


    $alphabet = range('a','z');
    
    print_r(generate_strings($alphabet,1));
    // expected output: A,B,C,...Z
    
    print_r(generate_strings($alphabet,2));
    // expected output: A..Z, AB..AZ, BC..BZ, CD, ..YZ
    
    print_r(generate_strings($alphabet,3));
    // expected output: A..Z, AB..YZ, ABC..XYZ
    
    print_r(generate_strings($alphabet,10));
    // expected output: A .. JKLMN .. AGKQRZ .. QRSTUVWXYZ
    //                        ^         ^          ^10 character max, no repeats
    //                        |         still alphabetical order
    //                        alphabetical order
    
    function generate_strings($alphabet,$max_word_length) {
    
        // how can I efficiently generate this array
        // without brute force looping through all of
        // the invalid and duplicate items like AA and CBA?
    
        return $array_of_all_possible_strings;
    }
    

3 个答案:

答案 0 :(得分:2)

我认为这看起来很有趣。这是我对它的尝试,因为它值得:

function recurse($letters, &$words, $start, $end, $depth, $prefix = "") {
    $depth--;
    for ($i = $start; $i < $end; $i++) {
        $word = $prefix . $letters[$i];
        $words[] = $word;
        if ($depth) recurse($letters, $words, ++$start, $end, $depth, $word);
    }
}
function generate_strings($letters, $max_word_length) {
    $words = array();
    recurse($letters, $words, 0, count($letters), $max_word_length);
    return $words;      
}

答案 1 :(得分:1)

不知道php,但算法很明确:

  1. 将N个字母(如果尚未)排序为数组。
  2. 维护两个动态数组:长度为N或更少(您想要的)的所有组合的列表,以及长度为N-1或更少的所有字符串的列表。
  3. 通过最后一个字符的字符数组向后
  4. 将第i个字符添加到所有字符串列表中作为单字符字符串。如果N> 1,还要添加到长度为N-1的字符串列表中。 1。
    1. 现在循环遍历长度为N-1或更少的字符串列表,从数组的开头到数组的当前结束
    2. 对于每个较短的字符串,请通过添加第i个字符来创建新字符串。
    3. 将此新字符串添加到所有字符串列表中。
    4. 如果长度为N -1或更小,则添加到较短字符串列表中。 (在这里注意迭代器 - 你不想访问刚刚在这个内循环中添加的字符串。)
  5. 返回字符串列表。

答案 2 :(得分:0)

我今天编辑了this similar post(因为我意识到我最初发布了一个不正确的方法),在研究这个主题时,我发现你的问题很有趣。

在我能说的时候,不能使用LeavePanic的递归方法。我只是想我会发布一个基于我的链接方法的非递归“堆叠”方法。我相信我们的解决方案之间几乎没有性能差异,也许你会发现我的解决方案更容易阅读(也许不是)。

代码:(Demo

function permStacker($array,$length){
    $stack=[[]];  // declare intitial empty element
    for($x=0; $x<$length; ++$x){  // limit the total number of passes / max string length
        foreach($stack as $combination){
            foreach(array_diff($array,range('a',end($combination))) as $left){  // do not include iterate letter that come earlier than rightmost letter
                $merge=array_merge($combination,[$left]);
                $stack[implode($merge)]=$merge;  // keys hold desired strings, values hold subarray of combinations for iterated referencing
            }
        }
    }
    unset($stack[0]); // remove initial empty element
    return array_keys($stack);  // return array of permutations as space delimited strings
}

$permutations=permStacker(range('a','h'),4);
echo 'Total Permutations: ',sizeof($permutations),"\n";
var_export($permutations);

输出(截断):

Total Permutations: 162
array (
  0 => 'a',
  1 => 'b',
  2 => 'c',
  3 => 'd',
  4 => 'e',
  5 => 'f',
  6 => 'g',
  7 => 'h',
  8 => 'ab',
  9 => 'ac',
  10 => 'ad',
  11 => 'ae',
  12 => 'af',
  13 => 'ag',
  14 => 'ah',
  15 => 'bc',
  16 => 'bd',
  17 => 'be',
  18 => 'bf',
  19 => 'bg',
  20 => 'bh',
  21 => 'cd',
  22 => 'ce',
  23 => 'cf',
  24 => 'cg',
  25 => 'ch',
  26 => 'de',
  ...
  126 => 'afgh',
  127 => 'bcde',
  128 => 'bcdf',
  129 => 'bcdg',
  130 => 'bcdh',
  131 => 'bcef',
  132 => 'bceg',
  133 => 'bceh',
  134 => 'bcfg',
  135 => 'bcfh',
  136 => 'bcgh',
  137 => 'bdef',
  138 => 'bdeg',
  139 => 'bdeh',
  140 => 'bdfg',
  141 => 'bdfh',
  142 => 'bdgh',
  143 => 'befg',
  144 => 'befh',
  145 => 'begh',
  146 => 'bfgh',
  147 => 'cdef',
  148 => 'cdeg',
  149 => 'cdeh',
  150 => 'cdfg',
  151 => 'cdfh',
  152 => 'cdgh',
  153 => 'cefg',
  154 => 'cefh',
  155 => 'cegh',
  156 => 'cfgh',
  157 => 'defg',
  158 => 'defh',
  159 => 'degh',
  160 => 'dfgh',
  161 => 'efgh',
)