字符串 全排列生成問題


轉自:http://blog.csdn.net/zinss26914/article/details/8939140

問題

  輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則輸出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba

思路

   1.這是典型的遞歸求解問題,遞歸算法有四個特性:
  • 必須有可達到的終止條件,否則程序陷入死循環
  • 子問題在規模上比原問題小
  • 子問題可通過再次遞歸調用求解
  • 子問題的解應能組合成整個問題的解
   2.對於字符串的排列問題:
  如果能生成n-1個元素的全排列,就能生成n個元素的全排列。對於只有一個元素的集合,可以直接生成全排列。所以全排列的遞歸終止條件很明確,只有一個元素時。我們可以分析一下全排列的過程:

  (1)首先,我們固定第一個字符a,求后面兩個字符bc的排列

  (2)當兩個字符bc排列求好之后,我們把第一個字符a和后面的b交換,得到bac,接着我們固定第一個字符b,求后面兩個字符ac的排列

  (3)現在是把c放在第一個位置的時候了,但是記住前面我們已經把原先的第一個字符a和后面的b做了交換,為了保證這次c仍是和原先處在第一個位置的a交換,我們在拿c和第一個字符交換之前,先要把b和a交換回來。在交換b和a之后,再拿c和處於第一位置的a進行交換,得到cba。我們再次固定第一個字符c,求后面兩個字符b、a的排列

  (4)既然我們已經知道怎么求三個字符的排列,那么固定第一個字符之后求后面兩個字符的排列,就是典型的遞歸思路了

  下面這張圖很清楚的給出了遞歸的過程:

  
去掉重復的全排列

  如果輸入字符有重復字符,就是會造成重復數據的輸出,例如abb這種字符串,基於上訴討論結果如圖:

   

   由於全排列就是從第一個數字起,每個數分別與它后面的數字交換,我們先嘗試加個這樣的判斷——如果一個數與后面的數字相同那么這兩個數就不交換了。例如abb,第一個數與后面兩個數交換得bab,bba。然后abb中第二個數和第三個數相同,就不用交換了。但是對bab,第二個數和第三個數不同,則需要交換,得到bba。由於這里的bba和開始第一個數與第三個數交換的結果相同了,因此這個方法不行。

   換種思維,對abb,第一個數a與第二個數b交換得到bab,然后考慮第一個數與第三個數交換,此時由於第三個數等於第二個數,所以第一個數就不再用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換可以解決bba。此時全排列生成完畢!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct seq{
    char str[7];
};

struct seq seqs[800];
int count;

int is_swap(char *str, int begin, int k){   ///判斷是否需要交換
    int i, flag;

    for (i = begin, flag = 1; i < k; i ++) {
        if (str[i] == str[k]) {
            flag = 0;
            break;
        }
    }

    return flag;
}

void swap(char *str, int a, int b)  ///交換字母
{
    char temp;
    temp = str[a];
    str[a] = str[b];
    str[b] = temp;
}

void permutation_process(char *name, int begin, int end) {
    int k;

    if (begin == end - 1)   ///遞歸出口
    {
        strcpy(seqs[count].str, name);  ///進行保存,用於后續的輸出
        count ++;
    }
    else
    {
        for (k = begin; k < end; k ++)
        {
            if (is_swap(name, begin, k)) ///判斷是否要進行交換。如果沒有交換,則忽略這種情況             {
                swap(name, k, begin);   ///字母交換
                permutation_process(name, begin + 1, end);  ///對[begin + 1,end)進行全排列
                swap(name, k, begin);   ///字母換回
            }
        }
    }
}

int compare(const void *p, const void *q)
{
    const char *a = (char*)p;
    const char *b = (char*)q;
    return strcmp(a, b);
}

int main()
{
    char name[7];   ///保存要進行全排列的字符串,長度應較小(小於7)
    int i, len;

    while (scanf("%s", name) != EOF) {
        count = 0;
        len = strlen(name);
        permutation_process(name, 0, len);  ///全排列
        qsort(seqs, count, sizeof(seqs[0]), compare);   ///將生成的全排列排序

        for (i = 0; i < count; i ++)
            printf("%s\n", seqs[i].str);
        printf("\n");
    }

    return 0;
}

輸入:abca

輸出:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM