[LeetCode] 267. Palindrome Permutation II 回文全排列之二


 

Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form.

Example 1:

Input: "aabb"
Output: ["abba", "baab"]

Example 2:

Input: "abc"
Output: []

Hint:

  1. If a palindromic permutation exists, we just need to generate the first half of the string.
  2. To generate all distinct permutations of a (half of) string, use a similar approach from: Permutations II or Next Permutation.

 

這道題是之前那道 Palindrome Permutation 的拓展,那道題只是讓判斷存不存在回文全排列,而這題讓返回所有的回文全排列,此題給了我們充分的提示:如果回文全排列存在,只需要生成前半段字符串即可,后面的直接根據前半段得到。那么再進一步思考,由於回文字符串有奇偶兩種情況,偶數回文串例如 abba,可以平均分成前后半段,而奇數回文串例如 abcba,需要分成前中后三段,需要注意的是中間部分只能是一個字符,可以分析得出,如果一個字符串的回文字符串要存在,那么奇數個的字符只能有0個或1個,其余的必須是偶數個,所以可以用哈希表來記錄所有字符的出現個數,然后找出出現奇數次數的字符加入 mid 中,如果有兩個或兩個以上的奇數個數的字符,則返回空集,對於每個字符,不管其奇偶,都將其個數除以2的個數的字符加入t中,這樣做的原因是如果是偶數個,將其一般加入t中,如果是奇數,如果有1個,除以2是0,不會有字符加入t,如果是3個,除以2是1,取一個加入t。等獲得了t之后,t是就是前半段字符,對其做全排列,每得到一個全排列,加上 mid 和該全排列的逆序列就是一種所求的回文字符串,這樣就可以得到所有的回文全排列了。在全排序的子函數中有一點需要注意的是,如果直接用數組來保存結果時,並且t中如果有重復字符的話可能會出現重復項,比如 t = "baa" 的話,那么最終生成的結果會有重復項,不信可以自己嘗試一下。這里簡單的說明一下,當 start=0,i=1 時,交換后得到 aba,在之后當 start=1,i=2 時,交換后可以得到 aab。但是在之后回到第一層當baa后,當 start=0,i=2 時,交換后又得到了 aab,重復就產生了。那么其實最簡單當去重復的方法就是將結果 res 定義成 HashSet,利用其去重復的特性,可以保證得到的是沒有重復的,參見代碼如下:

 

解法一:

class Solution {
public:
    vector<string> generatePalindromes(string s) {
        unordered_set<string> res;
        unordered_map<char, int> m;
        string t = "", mid = "";
        for (auto a : s) ++m[a];
        for (auto it : m) {
            if (it.second % 2 == 1) mid += it.first;
            t += string(it.second / 2, it.first);
            if (mid.size() > 1) return {};
        }
        permute(t, 0, mid, res);
        return vector<string>(res.begin(), res.end());
    }
    void permute(string &t, int start, string mid, unordered_set<string> &res) {
        if (start >= t.size()) {
            res.insert(t + mid + string(t.rbegin(), t.rend()));
        } 
        for (int i = start; i < t.size(); ++i) {
            if (i != start && t[i] == t[start]) continue;
            swap(t[i], t[start]);
            permute(t, start + 1, mid, res);
            swap(t[i], t[start]);
        }
    }
};

 

下面這種方法和上面的方法很相似,不同之處來於求全排列的方法略有不同,上面那種方法是通過交換字符的位置來生成不同的字符串,而下面這種方法是通過加不同的字符來生成全排列字符串,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<string> generatePalindromes(string s) {
        vector<string> res;
        unordered_map<char, int> m;
        string t = "", mid = "";
        for (auto a : s) ++m[a];
        for (auto &it : m) {
            if (it.second % 2 == 1) mid += it.first;
            it.second /= 2;
            t += string(it.second, it.first);
            if (mid.size() > 1) return {};
        }
        permute(t, m, mid, "", res);
        return res;
    }
    void permute(string &t, unordered_map<char, int> &m, string mid, string out, vector<string> &res) {
        if (out.size() >= t.size()) {
            res.push_back(out + mid + string(out.rbegin(), out.rend()));
            return;
        } 
        for (auto &it : m) {
            if (it.second > 0) {
                --it.second;
                permute(t, m, mid, out + it.first, res);
                ++it.second;
            }
        }
    }
};

 

在來看一種利用了 std 提供的 next_permutation 函數來實現的方法,這樣就大大減輕了我們的工作量,但是這種方法個人感覺算是有些投機取巧了,不知道面試的時候面試官允不允許這樣做,貼上來拓寬一下思路也是好的:

 

解法三:

class Solution {
public:
    vector<string> generatePalindromes(string s) {
        vector<string> res;
        unordered_map<char, int> m;
        string t = "", mid = "";
        for (auto a : s) ++m[a];
        for (auto it : m) {
            if (it.second % 2 == 1) mid += it.first;
            t += string(it.second / 2, it.first);
            if (mid.size() > 1) return {};
        }
        sort(t.begin(), t.end());
        do {
            res.push_back(t + mid + string(t.rbegin(), t.rend()));
        } while (next_permutation(t.begin(), t.end()));
        return res;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/267

 

類似題目:

Next Permutation

Palindrome Permutation

Permutations II

Permutations

 

參考資料:

https://leetcode.com/problems/palindrome-permutation-ii/

https://leetcode.com/problems/palindrome-permutation-ii/discuss/69696/AC-Java-solution-with-explanation

https://leetcode.com/problems/palindrome-permutation-ii/discuss/69698/Short-backtracking-solution-in-Java-(3-ms)

https://leetcode.com/problems/palindrome-permutation-ii/discuss/69767/22-lines-0ms-C%2B%2B-easy-to-understand

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

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



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