一、题目
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = “qqe”
输出:[“eqq”,“qeq”,“qqe”]
示例2:
输入:S = “ab”
输出:[“ab”, “ba”]
提示:
- 字符都是英文字母。
- 字符串长度在[1, 9]之间。
二、C# 题解
同题目 LeetCode 面试题 08.07. 无重复字符串的排列组合,进行剪枝:
- 元素 S[i] 与 S[n] 相同时,舍去;
- 所得为新结果时,才纳入 ans。
步骤 1 剪枝不完全,所以还需加上步骤 2。
public class Solution {
public string[] Permutation(string S) {
List<string> ans = new List<string>();
Partition(ans, new StringBuilder(S), 0);
return ans.ToArray();
}
public void Partition(List<string> ans, StringBuilder sb, int n) {
if (n == sb.Length) {
ans.Add(sb.ToString());
return;
}
HashSet<char> hs = new HashSet<char>();
for (int i = n; i < sb.Length; i++) {
if (hs.Contains(sb[i])) continue;
hs.Add(sb[i]);
(sb[i], sb[n]) = (sb[n], sb[i]);
Partition(ans, sb, n + 1);
(sb[i], sb[n]) = (sb[n], sb[i]);
}
}
}
- 时间:140 ms,击败 71.43% 使用 C# 的用户
- 内存:46.7 MB,击败 92.86% 使用 C# 的用户
另一种剪枝方式,用一个 Set 集合存放已经交换过的重复元素,即不让当前 S[n] 与已经交换过的字母再进行交换:
public class Solution {
public string[] Permutation(string S) {
List<string> ans = new List<string>();
Partition(ans, new StringBuilder(S), 0);
return ans.ToArray();
}
public void Partition(List<string> ans, StringBuilder sb, int n) {
if (n == sb.Length) { // 递归出口
ans.Add(sb.ToString());
return;
}
HashSet<char> hs = new HashSet<char>(); // 记录已交换过的元素
for (int i = n; i < sb.Length; i++) {
if (hs.Contains(sb[i])) continue; // 该元素已被交换过,则跳过
hs.Add(sb[i]); // 添加记录
(sb[i], sb[n]) = (sb[n], sb[i]);
Partition(ans, sb, n + 1);
(sb[i], sb[n]) = (sb[n], sb[i]);
}
}
}
- 时间:128 ms,击败 100.00% 使用 C# 的用户
- 内存:46.38 MB,击败 78.57% 使用 C# 的用户