滚雪球思路 计算一个字符串所有可能的排列顺序
今天一同学丢了个问题给我,怎么列出一个字符串所有的排列可能性
刚开始的思路是,先取第一位不同,比如abcd
那取第一位不同的话就有四种可能abcd、bacd、cbad、dcba
这样就想到了用for去循环4次,然后再对后面的组合进行排列
比如abcd,分别取后面三个字母,每个字母出现的位置有三种可能
如:b的话,三个位置为abcd、acbd、acdb
因为结果有重复数据,所以用上了array_unique排重,
后来才发现漏了点什么,因为每次算出来的结果排重后数量都不对
仔细研这分一下发现问题所在
比如abcd中b在第一个位置时,我只算了abcd,漏掉了一种可能就是abdc
至此,前面的想法全部抛弃了。。。
从错误中发现一个问题,就是每两个字母都有可能调整换位置
换言之,要排列所有的可能,就要从最小的组合开始
那么就从两个字母开始排列,假设a和b
可能的顺序为ab 和ba
这时候如果再多一个字母c
那么可能出现的排列变为 cab acb abc和 cba bca bac
从上面能看出来,从最小的组合开始,每当加入一个新的字母时的规律为
用新加的字母对原来的组合中的每一个成员,如上面的ab和ba,分别插入
插入后再将新插入的字母所有可能插入的位置进行计算,
如上面的ab插入c后,c可能出现在左中右
这里可以用一个for循环来完成,
1 $str = 'ab'; 2 $newstr = 'c'.$str; 3 $len = strlen($str); 4 //考虑到后面还要经常使用现在算出来的结果,所以用了一个数组来放置结果 5 $arr = array(); 6 for($i=0;$i<$len;$i++){ 7 $arr[] = strtr($newstr,array($newstr[0]=>$newstr[$i],$newstr[$i]=>$newstr[0])); 8 9 }
如果只加了一个c,那么这样每次计算出来的结果就是我们想要的结果了
现在给定的字符串是未知的,所以要考虑到一直添加新的字符串到最小的组合中
我想到了用递归来完成这个操作,码出代码如下:
1 $str = 'abcd'; 2 $len = strlen($str); 3 $arr = array($str[$len-1]); 4 $return = ''; 5 getStr($str,$arr,$len-2,$return); 6 print_r($return); 7 8 function getStr($str,$arr,$n,&$return){ 9 if($n>=0){ 10 $newarr = array(); 11 foreach($arr as $v){ 12 $temp = $str[$n].$v; 13 $l = strlen($temp); 14 for($i=0;$i<$l;$i++){ 15 $newarr[] = strtr($temp,array($temp[0]=>$temp[$i],$temp[$i]=>$temp[0])); 16 } 17 } 18 getStr($str,$newarr,$n-1,$return); 19 } else { 20 $return = $arr; 21 } 22 }
写完之后发现用递归有些浪费了,完全没有必要。。。对上面的代码做了下优化,最终结果如下:
1 $str = 'abc'; 2 $return = getStr($str); 3 print_r($return); 4 function getStr($str){ 5 $len = strlen($str); 6 $arr = array($str[0]); 7 for($i=1;$i<$len;$i++){ 8 $newarr = array(); 9 foreach($arr as $v){ 10 $temp = $str[$i].$v; 11 $l = strlen($temp); 12 for($j=0;$j<$l;$j++){ 13 $newarr[] = strtr($temp,array($temp[0]=>$temp[$j],$temp[$j]=>$temp[0])); 14 } 15 } 16 $arr = $newarr; 17 } 18 return $arr; 19 }
E-mail:cnbeir@163.com