【剑指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;
}
};