leetcode
leetcode 1001 ~ 1050
活字印刷

活字印刷

难度:

标签:

题目描述

代码结果

运行时间: 28 ms, 内存: 16.1 MB


// 思路:使用回溯法与Java Stream API结合。首先计算每种排列组合,并将其收集到一个集合中,最后计算集合的大小。

import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class TilePossibilitiesStream {

    public int numTilePossibilities(String tiles) {
        Set<String> result = new HashSet<>();
        boolean[] used = new boolean[tiles.length()];
        backtrack(tiles, used, new StringBuilder(), result);
        return result.size();
    }

    private void backtrack(String tiles, boolean[] used, StringBuilder current, Set<String> result) {
        if (current.length() > 0) {
            result.add(current.toString());
        }
        IntStream.range(0, tiles.length()).filter(i -> !used[i]).forEach(i -> {
            used[i] = true;
            current.append(tiles.charAt(i));
            backtrack(tiles, used, current, result);
            current.deleteCharAt(current.length() - 1);
            used[i] = false;
        });
    }

    public static void main(String[] args) {
        TilePossibilitiesStream solution = new TilePossibilitiesStream();
        System.out.println(solution.numTilePossibilities("AAB")); // 输出 8
    }
}

解释

方法:

这道题可以使用动态规划的方法来解决。首先,我们使用Counter来统计每个字母出现的次数。然后,定义一个二维数组f,其中f[i][j]表示使用前i个不同字母,组成长度为j的字符串的方法数。我们可以通过枚举前i-1个字母组成长度为j-c的字符串,然后从第i个字母中选出c个来组成长度为j的字符串,来更新f[i][j]。最后,将长度为1到n的所有字符串的方法数相加,得到最终结果。

时间复杂度:

O(m*n^2)

空间复杂度:

O(m*n)

代码细节讲解

🦆
在动态规划解法中,二维数组f[i][j]的具体含义是什么?每个维度代表的是什么?
在这个动态规划解法中,二维数组f[i][j]表示考虑前i个不同的字母,能够组成长度为j的所有不同字符串的组合数。其中,i代表考虑的不同字母的数量,j代表目标字符串的长度。这种表示方式使我们能够逐步构建解决方案,通过增加字母和调整长度来递推计算最终的结果。
🦆
为什么初始化f[0][0]为1,而其他f[0][j](j>0)的值是什么?这样的初始化有什么特别的意义吗?
初始化f[0][0]为1是因为使用0个字母可以唯一的方式形成长度为0的字符串(即空字符串)。而对于所有j>0的情况,f[0][j]被初始化为0,因为使用0个字母无法形成任何非零长度的字符串。这样的初始化对于动态规划算法的基准情况至关重要,确保了递推开始时有一个正确的起点。
🦆
在计算组合数comb(j, c)时,如何保证计算的正确性和效率?是否有可能引入额外的库或函数来实现这一计算?
计算组合数comb(j, c)需要准确和高效。在Python中,可以使用`math.comb`函数来直接计算组合数,该函数是自Python 3.8起提供的,能够有效地计算组合数而不需要手动实现。这不仅保证了计算的正确性,由于它是优化过的,也保证了计算的效率。如果使用早于3.8版本的Python,可能需要自己实现组合数的计算或使用第三方库如`scipy.special.comb`。
🦆
在内层循环使用`min(j, k) + 1`的边界条件进行迭代时,这样的选择是基于什么考虑?
使用`min(j, k) + 1`作为迭代的边界条件是基于这样的考虑:我们不能从一个字母中选择超过它出现次数(k)或者当前目标字符串长度(j)的字符数。因此,`min(j, k)`确保了不会选择超过可用数量的字符,而`+1`是因为范围包括从0个字符开始选择,直到最大可用字符数。这种选择在确保不超过字母使用次数的同时也避免了不必要的迭代,提高了效率。

相关问题