1. 程式人生 > >【演算法基礎】字串的全排列演算法

【演算法基礎】字串的全排列演算法

題目描述

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

輸入一個字串,長度不超過9(可能有字元重複),字元只包括大小寫字母。

這道題是劍指offfer中一道經典的題,如果有n個元素,我們就能有n*(n - 1)個全排列,n為1時,只有一個全排列

舉個栗子,當字串為ABC時,分析一下全排列的過程:

1.固定A在字串的最左邊,然後全排列B和C

2.全排列B和C

3.當全排列的字串只有一個字元時,它的全排列就是它自身

4.當固定A後,其他字串的全排列完畢後,把A與其他字元進行交換,然後重複上面的步驟進行全排列

示意圖如下:

基於這種思路,我使用回溯法解決,程式碼如下:

class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> ret;
        if (str.empty()) {
            return ret;
        }
        string tmp = "";
        _permutation(str, ret, tmp, 0);
        return ret;
    }
private:
    void _permutation(string str, vector<string>& ret, string& tmp, int begin) {
        if (begin == str.size()) {
            ret.push_back(tmp);
            return;
        }
        for (int i = begin; i < str.size(); ++i) {
            if (i != begin && str[i] == str[begin])
                continue;
            swap(str[i], str[begin]);
            tmp += str[begin];
            _permutation(str, ret, tmp, begin + 1);
            tmp.pop_back();
        }
    }
};

這裡主要解釋一下這個函式:

void _permutation(string str, vector<string>& ret, string& tmp, int begin) {
        if (begin == str.size()) {
            ret.push_back(tmp);
            return;
        }
        for (int i = begin; i < str.size(); ++i) {
            if (i != begin && str[i] == str[begin])
                continue;
            swap(str[i], str[begin]);
            tmp += str[begin];
            _permutation(str, ret, tmp, begin + 1);
            tmp.pop_back();
        }
    }

這個函式的思路就是上面的思路,使用回溯法,思路如下

1. 讓當前字元去和每個字串交換

2.交換完成後,遞迴的呼叫函式去處理除當前字元之外剩下的字元

遞迴的出口就是begin等於str.size(),這時候的tmp(因為一直在儲存每一個字元),就是一個全排列的結果,儲存在陣列ret之中

注意,在迴圈中有這麼一個判斷:

if (i != begin && str[i] == str[begin])
    continue;

這個判斷主要是處理字串中有重複字元的

去重的全排列就是從第一個數字起,每個數分別與它後面非重複出現的數字交換

在這個迴圈遍歷時,有兩種情況str[i]和str[begin]相等,一種是i和begin相等,即同一個元素,一種是相同的元素

這時候如果交換的話沒有意義,就不用交換並且儲存了,不然就會出現重複的結果

舉個栗子,當字串為aabb時,如果註釋掉這句話,ret的結果是

如果不註釋掉,結果是正確的:

如果要按照字典序輸出的話,還需要對陣列ret進行排序

再介紹一種簡單的方法,如果為了快速AC的話,可以使用STL中的next_permutation函式,函式定義如下:

template <class BidirectionalIterator>
  bool next_permutation (BidirectionalIterator first,
                         BidirectionalIterator last);

template <class BidirectionalIterator, class Compare>
  bool next_permutation (BidirectionalIterator first,
                         BidirectionalIterator last, Compare comp);

解決的程式碼如下:

vector<string> Permutation(string str) {
        vector<string> answer;
        if(str.empty())
            return answer;       
        sort(str.begin(),str.end());
        do{
            answer.push_back(str);
        }
        while(next_permutation(str.begin(),str.end()));
        return answer;
}

這個函式的文件連結如下:

http://www.cplusplus.com/reference/algorithm/next_permutation/?kw=next_permutation
---------------------  
作者:Qregi  
來源:CSDN  
原文:https://blog.csdn.net/Qregi/article/details/82049298  
版權宣告:本文為博主原創文章,轉載請附上博文連結!