题目描述
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/
题解
回溯
对给定字符串进行排序,对每一个位置选择一个字符放置,并使用 used
数组确保每个字符只能选一次。
特别的,如果字符串中有相同的字符,比如 “aabc” 中的 “aa”,为方便,我们标记为 “a1a2bc”,可能生成的排列包含 “a1a2bc” 和 “a2a1bc”,但是都为 “aabc”。
这时要保证生成的排列不含重复排列,我们可以生成后再去重(或使用 set),也可以保证这段相同字符的排列唯一,可以规定相同字符必须从左到右排列。比如上面的例子中,无论生成什么排列,我们单独看重复字符 ‘a’ 的排列,都为 “a1a2”,如 “a1a2bc”、“a1ba2c”、“a1bca2”、“ba1a2c”、“ba1ca2”、“bca1a2”。这样就不会因为重复字符的存在而生成重复排列了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { int n; string path; vector<string> ans; void backtrack(const string& s, vector<bool> used, int i) { if (i == n) { ans.push_back(path); return; } for (int j = 0; j < n; j++) { if (used[j] || j > 0 && s[j] == s[j - 1] && !used[j - 1]) continue; path.push_back(s[j]); used[j] = true; backtrack(s, used, i + 1); path.pop_back(); used[j] = false; } } public: vector<string> permutation(string s) { n = s.size(); sort(s.begin(), s.end()); vector<bool> used(n, false); backtrack(s, used, 0); return ans; } };
|
时间复杂度:O(n×n!),其中 n 为给定字符串的长度。这些字符的全部排列有 O(n!) 个,每个排列平均需要 O(n) 的时间来生成。
空间复杂度:O(n)。我们需要 O(n) 的栈空间进行回溯,注意返回值不计入空间复杂度。
下一个排列
函数 next_permutation()
已知一个排列,快速得到字典序中的下一个更大的排列。
同样是对字符串排序,获得字典序最小的排列,然后每次获得下一个更大排列,一直到没有更大排列。
C++ 为我们提供了 next_permutation()
,当然也可以自己实现它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { bool next_permutation(string& s) { int i = s.size() - 2; while (i >= 0 && s[i] >= s[i + 1]) i--; if (i >= 0) { int j = s.size() - 1; while (s[j] <= s[i]) j--; swap(s[i], s[j]); reverse(s.begin() + i + 1, s.end()); return true; } return false; } public: vector<string> permutation(string s) { sort(s.begin(), s.end()); vector<string> ans{s}; while (next_permutation(s)) ans.push_back(s); return ans; } };
|
时间复杂度:O(n×n!),其中 n 为给定字符串的长度。我们需要 O(nlogn) 的时间得到第一个排列,nextPermutation 函数的时间复杂度为 O(n),我们至多执行该函数 O(n!) 次。
空间复杂度:O(1)。注意返回值不计入空间复杂度。