【剑指Offer】面试题38. 字符串的排列

题目

输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

限制:1 <= s 的长度 <= 8

思路一:回溯+无序set去重

将字符串分为两部分,第一部分为第一个字符,第二部分为后面所有字符。求整个字符串的全排列,可以看成两步,第一步求所有可能出现在第一个位置的字符,即把第一个字符和后面所有字符交换。第二步,固定第一个字符,求后面所有字符排列。
比如字符串“abc”,
第一部分为字符a,第二部分为“bc”,首先a在第一个字符,求字符串“bc”的所有全排列得到:
abc
acb
然后交换a和b,b放在第一个字符,求"ac"所有全排列得到:
bac
bca
还原a和b
再交换a和c,c放在第一个字符,求“ba”所有全排列得到:
cba
cab

代码

class Solution {
    unordered_set<string> st;
public:
    vector<string> permutation(string s) {
        vector<string> res;        
        if (s.empty()) return res;                    
        dfs(s, 0, res);
        return res;
    }

    void dfs(string &s, int i, vector<string> &res) {        
        if (i == s.size()) {
            if (st.count(s) == 0) {
                res.push_back(s);
                st.insert(s);
            }
            return;
        }
        for (int j = i; j < s.size(); ++j) {
            swap(s[j], s[i]);  // 第一个字符和后面所有字符交换
            dfs(s, i + 1, res);  // 求后面字符所有排列
            swap(s[j], s[i]);  // 回溯
        }
    }
};

优化

利用函数判断重复元素

class Solution {    
public:
    vector<string> permutation(string s) {
        vector<string> res;                
        if (s.empty()) return res;                     
        helper(s, 0, res);
        return res;
    }

    void helper(string &s, int pos, vector<string> &res) {        
        if (pos == s.size()) {
            res.push_back(s);
            return;
        } 
        for (int i = pos; i < s.size(); ++i) {
            if (dup(s, pos, i)) continue;  // 在pos到i之间存在重复元素则跳过
            swap(s[pos], s[i]);
            helper(s, pos + 1, res);
            swap(s[pos], s[i]);
        }
    }

    bool dup(string &s, int start, int end) {
        for (int i = start; i < end; ++i) {
            if (s[i] == s[end]) return true;
        }
        return false; 
    }
};
posted @ 2020-05-14 22:54  Galaxy_hao  阅读(194)  评论(0编辑  收藏  举报