最近做了一道阿里的筆試題
1. 字符串“alibaba”有 個不同的排列。
A. 5040 B. 840 C. 14 D.420
用概率的辦法可以直接求解出C73*C42*A22,C73,7是下標,3是上標,結果是420;
后來查了一下,這是一個全排列的問題,於是學習了一下全排列的算法。
學習的博客http://blog.csdn.net/wzy_1988/article/details/8939140
問題
輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則輸出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba
正常人的思維是,固定第一個字符,然后依次將后面的字符串與前面的交換,那么排列的個數就是除了第一個字符以外,其他字符的排列個數+1。
也就是固定一個字符串之后,之后再將問題變小,只需求出后面子串的排列個數就可以得出結果,當然第一時間想到的就是遞歸的算法了。
下面這張圖很清楚的給出了遞歸的過程:
很明顯,遞歸的出口,就是只剩一個字符的時候,遞歸的循環過程,就是從每個子串的第二個字符開始依次與第一個字符交換,然后繼續處理子串。
還有一個問題要注意,就是如果字符串中有重復的字符串
由於全排列就是從第一個數字起,每個數分別與它后面的數字交換,我們先嘗試加個這樣的判斷——如果一個數與后面的數字相同那么這兩個數就不交換 了。例如abb,第一個數與后面兩個數交換得bab,bba。然后abb中第二個數和第三個數相同,就不用交換了。但是對bab,第二個數和第三個數不 同,則需要交換,得到bba。由於這里的bba和開始第一個數與第三個數交換的結果相同了,因此這個方法不行。
換種思維,對abb,第一個數a與第二個數b交換得到bab,然后考慮第一個數與第三個數交換,此時由於第三個數等於第二個數,所以第一個數就不再用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換可以解決bba。此時全排列生成完畢!
這樣,我們得到在全排列中去掉重復的規則:
去重的全排列就是從第一個數字起,每個數分別與它后面非重復出現的數字交換。
所以代碼如下
1 #include <stdio.h> 2 3 static int count = 0; 4 5 void swap(char* str,int a,int b) 6 { 7 char tmp = str[a]; 8 str[a] = str[b]; 9 str[b] = tmp; 10 } 11 12 13 int is_swap(char *str, int begin, int k){ //判斷從子串的第一個字符串開始,直到k-1位置,看是否有重復的字符 14 int i, flag; 15 16 for (i = begin, flag = 1; i < k; i ++) { 17 if (str[i] == str[k]) { 18 flag = 0; 19 break; 20 } 21 } 22 23 return flag; 24 } 25 26 void full_permutation(char* str,int begin,int end) 27 { 28 if (begin == end) 29 { 30 count++;//此處可以輸出字符串或者記錄字符串 31 return; 32 }else{ 33 int i; 34 for (i = begin; i <= end; i++) 35 { 36 if (is_swap(str,begin,i)) 37 { 38 swap(str,begin,i); 39 full_permutation(str,begin+1,end); 40 swap(str,begin,i); 41 } 42 } 43 } 44 } 45 46 int main() 47 { 48 char str[7] = {'a','l','i','b','a','b','a'}; 49 full_permutation(str,0,6); 50 printf("count=%d",count); 51 return 0; 52 }
運行結果
