【编程题目 | 100分】全排列 [ 100 / 中等 ]
全排列
题目描述:
给定一个只包含大写英文字母的字符串S,要求你给出对S重新排列的所有不相同的排列数。
如:S为ABA,则不同的排列有ABA、AAB、BAA三种。
解答要求
时间限制:5000ms, 内存限制:100MB
输入描述
输入一个长度不超过10的字符串S,确保都是大写的。
输出描述
输出S重新排列的所有不相同的排列数(包含自己本身)。
示例 1:
输入
ABA
输出
3
示例 2:
输入
ABCDEFGHHA
输出
907200
思路分析:
-
回溯DFS
全排列是回溯算法中比较常考的一个点,我的做法一般是定义一个栈,存储排列过的字符,一个布尔数组,判断字符是否使用过,一个结果集。递归是根据深度与数组的长度之间的关系。这道题有重复字符,需要剪枝。更详细的可以参考:Java实现回溯算法入门(排列+组合+子集)。
-
数学
先把每个字符当成唯一出现过一次,计算所有排列数;再统计重复出现的字母,除去每个字母的排列次数。例如:
参考代码:
Java代码实现:
- 回溯DFS
import java.util.*;
public class quanPaiLie {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
in.close();
char[] ch = str.toCharArray();
Arrays.sort(ch); // 排序是剪枝的前提
Deque<Character> path = new ArrayDeque<>(); // 用栈构造path,存储已经选择的字符
boolean[] used = new boolean[ch.length]; // 判断字符是否使用过
List<List<Character>> res = new ArrayList<>();
dfs(ch, 0, path, used, res);
System.out.println(res.size());
}
private static void dfs(char[] ch, int depth, Deque<Character> path, boolean[] used, List<List<Character>> res) {
if (depth == ch.length) { // 当遍历的层数等于数组长度时结束
res.add(new ArrayList<>(path));
return ;
}
for (int i = 0; i < ch.length; i++) {
if (used[i]) {
continue;
}
// 剪枝
if (i > 0 && ch[i] == ch[i - 1] && !used[i - 1]) {
continue;
}
path.addLast(ch[i]);
used[i] = true;
dfs(ch, depth + 1, path, used, res);
path.removeLast();
used[i] = false;
}
}
}
- 数学
import java.util.*;
public class ReSortChars {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
char[] chars = in.nextLine().toCharArray();
Map<Character, Integer> map = new HashMap<Character, Integer>();
int num = 0;
for (char ch:chars) {
map.put(ch, map.getOrDefault(ch, 0) + 1);
}
int allSort = SortOne(chars.length);
for (char key : map.keySet()) {
allSort = allSort/SortOne(map.get(key));
}
System.out.println(allSort);
}
private static int SortOne (int charsnum) {
if (charsnum == 1) {
return 1;
}
return charsnum * SortOne(charsnum - 1);
}
}