1079. 活字印刷【中等】
你有一套活字字模 tiles
,其中每个字模上都刻有一个字母 tiles[i]
。返回你可以印出的非空字母序列的数目。
**注意:**本题中,每个活字字模只能使用一次。
示例 1:
输入:“AAB”
输出: 8
解释: 可能的序列为 “A”, “B”, “AA”, “AB”, “BA”, “AAB”, “ABA”, “BAA”。
示例 2:
输入:“AAABBC”
输出: 188
关于构造全排列的代码请见:
本题看样例输出,既不像子集,也不像排列,但更像排列。
实际上本题求的是在构造***含有重复元素数组全排列***时,统计搜索树上的节点数,看下图一目了然:
在构造全排列时,根据递归的思想:每次只确定当前位置上的元素,递归确定下一个位置上的元素,搜索树上的节点由哪些已经确定的子排列构成。
上图中除了,根节点之外的所有节点就是使用AAB
所能印刷的所有序列。
下面的代码实际上就是递归法构造全排列,不过我们并不是需要真的构造出全排列,而是统计递归次数(也就是搜索树的节点数目),以及需要处理重复元素。
处理办法就是:在递归同一层的循环之中,只考虑首次出现的元素,这样做在搜索树的同一层不会出现重复结点。
private int ans;
private void swap(char[] chars, int i, int j) {
char ch = chars[i];
chars[i] = chars[j];
chars[j] = ch;
}
public void numTilePossibilities(char[] chars, int cur) {
if (cur == chars.length) return;
lable:
for (int i = cur; i < chars.length; i++) {
// 由于构造全排列使用的是交换法
// 递归过程中数组中的重复元素可能不相邻
// 因此不能使用 if(i!=cur && chars[i] == chars[i-1]) continue;
for (int j = cur; j < i; j++) {
// 递归同一层的循环之中,只考虑首次出现的元素
if (chars[j] == chars[i]) continue lable;
}
ans++;
if (i != cur) swap(chars, cur, i);
numTilePossibilities(chars, cur + 1);
if (i != cur) swap(chars, cur, i);
}
}
public int numTilePossibilities(String tiles) {
numTilePossibilities(tiles.toCharArray(), 0);
return ans;
}