列表

详情


1079. 活字印刷

你有一套活字字模 tiles,其中每个字模上都刻有一个字母 tiles[i]。返回你可以印出的非空字母序列的数目。

注意:本题中,每个活字字模只能使用一次。

 

示例 1:

输入:"AAB"
输出:8
解释:可能的序列为 "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA"。

示例 2:

输入:"AAABBC"
输出:188

示例 3:

输入:"V"
输出:1

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int numTilePossibilities(string tiles) { } };

golang 解法, 执行用时: 0 ms, 内存消耗: 1.8 MB, 提交时间: 2023-05-19 09:19:25

const mx = 8
var c [mx][mx]int

func init() {
    for i := 0; i < mx; i++ {
        c[i][0], c[i][i] = 1, 1
        for j := 1; j < i; j++ {
            c[i][j] = c[i-1][j-1] + c[i-1][j] // 预处理组合数
        }
    }
}

func numTilePossibilities(tiles string) (ans int) {
    counts := map[rune]int{}
    for _, ch := range tiles {
        counts[ch]++ // 统计每个字母的出现次数
    }
    f := make([]int, len(tiles)+1)
    f[0] = 1 // 构造空序列的方案数
    n := 0
    for _, cnt := range counts { // 枚举第 i 种字母
        n += cnt // 常数优化:相比从 tiles.length() 开始要更快
        for j := n; j > 0; j-- { // 枚举序列长度 j
            // 枚举第 i 种字母选了 k 个,注意 k=0 时的方案数已经在 f[j] 中了
            for k := 1; k <= j && k <= cnt; k++ {
                f[j] += f[j-k] * c[j][k]
            }
        }
    }
    for _, x := range f[1:] {
        ans += x
    }
    return
}

java 解法, 执行用时: 2 ms, 内存消耗: 39.6 MB, 提交时间: 2023-05-19 09:19:00

class Solution {
    private static final int MX = 8;
    private static final int[][] c = new int[MX][MX];

    static {
        for (int i = 0; i < MX; i++) {
            c[i][0] = c[i][i] = 1;
            for (int j = 1; j < i; j++)
                c[i][j] = c[i - 1][j - 1] + c[i - 1][j]; // 预处理组合数
        }
    }

    public int numTilePossibilities(String tiles) {
        // 注:改成 int[26] 统计可能会快一点点,感兴趣可以试试(下面 DP 跳过 cnt=0 的情况)
        var counts = new HashMap<Character, Integer>(); // 统计每个字母的出现次数
        for (var c : tiles.toCharArray())
            counts.merge(c, 1, Integer::sum); // counts[c]++
        var f = new int[tiles.length() + 1];
        f[0] = 1; // 构造空序列的方案数
        int n = 0;
        for (var cnt : counts.values()) { // 枚举第 i 种字母
            n += cnt; // 常数优化:相比从 tiles.length() 开始要更快
            for (int j = n; j > 0; j--) // 枚举序列长度 j
                // 枚举第 i 种字母选了 k 个,注意 k=0 时的方案数已经在 f[j] 中了
                for (int k = 1; k <= j && k <= cnt; k++)
                    f[j] += f[j - k] * c[j][k];
        }
        int ans = 0;
        for (int j = 1; j <= n; j++)
            ans += f[j];
        return ans;
    }
}

python3 解法, 执行用时: 40 ms, 内存消耗: 16.1 MB, 提交时间: 2023-05-19 09:18:47

# 空间优化
class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        f = [1] + [0] * len(tiles)
        n = 0
        for cnt in Counter(tiles).values():  # 枚举第 i 种字母
            n += cnt  # 常数优化:相比从 len(tiles) 开始要更快
            for j in range(n, 0, -1):  # 枚举序列长度 j
                # 枚举第 i 种字母选了 k 个,注意 k=0 时的方案数已经在 f[j] 中了
                for k in range(1, min(j, cnt) + 1):
                    f[j] += f[j - k] * comb(j, k)  # comb 也可以预处理,见其它语言的实现
        return sum(f[1:])

golang 解法, 执行用时: 0 ms, 内存消耗: 1.8 MB, 提交时间: 2023-05-19 09:18:10

const mx = 8
var c [mx][mx]int

func init() {
    for i := 0; i < mx; i++ {
        c[i][0], c[i][i] = 1, 1
        for j := 1; j < i; j++ {
            c[i][j] = c[i-1][j-1] + c[i-1][j] // 预处理组合数
        }
    }
}

func numTilePossibilities(tiles string) (ans int) {
    counts := map[rune]int{}
    for _, ch := range tiles {
        counts[ch]++ // 统计每个字母的出现次数
    }
    n, m := len(tiles), len(counts)
    f := make([][]int, m+1)
    f[0] = make([]int, n+1)
    f[0][0] = 1 // 构造空序列的方案数
    i := 1
    for _, cnt := range counts { // 枚举第 i 种字母
        f[i] = make([]int, n+1)
        for j := 0; j <= n; j++ { // 枚举序列长度 j
            for k := 0; k <= j && k <= cnt; k++ { // 枚举第 i 种字母选了 k 个
                f[i][j] += f[i-1][j-k] * c[j][k]
            }
        }
        i++
    }
    for _, x := range f[m][1:] {
        ans += x
    }
    return
}

java 解法, 执行用时: 1 ms, 内存消耗: 39.6 MB, 提交时间: 2023-05-19 09:17:56

class Solution {
    private static final int MX = 8;
    private static final int[][] c = new int[MX][MX];

    static {
        for (int i = 0; i < MX; i++) {
            c[i][0] = c[i][i] = 1;
            for (int j = 1; j < i; j++)
                c[i][j] = c[i - 1][j - 1] + c[i - 1][j]; // 预处理组合数
        }
    }

    public int numTilePossibilities(String tiles) {
        var counts = new HashMap<Character, Integer>(); // 统计每个字母的出现次数
        for (var c : tiles.toCharArray())
            counts.merge(c, 1, Integer::sum); // counts[c]++
        int m = counts.size(), n = tiles.length();
        var f = new int[m + 1][n + 1];
        f[0][0] = 1; // 构造空序列的方案数
        int i = 1;
        for (var cnt : counts.values()) { // 枚举第 i 种字母
            for (int j = 0; j <= n; j++) // 枚举序列长度 j
                for (int k = 0; k <= j && k <= cnt; k++) // 枚举第 i 种字母选了 k 个
                    f[i][j] += f[i - 1][j - k] * c[j][k];
            i++;
        }
        int ans = 0;
        for (int j = 1; j <= n; j++)
            ans += f[m][j];
        return ans;
    }
}

python3 解法, 执行用时: 44 ms, 内存消耗: 16 MB, 提交时间: 2023-05-19 09:17:42

# 动态规划,计数dp
# 定义 f[i][j] 表示用前 i 种字符构造长为 j 的序列的方案数。
class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        counts = Counter(tiles).values()  # 统计每个字母的出现次数
        n, m = len(tiles), len(counts)
        f = [[0] * (n + 1) for _ in range(m + 1)]
        f[0][0] = 1  # 构造空序列的方案数
        for i, cnt in enumerate(counts, 1):  # 枚举第 i 种字母
            for j in range(n + 1):  # 枚举序列长度 j
                for k in range(min(j, cnt) + 1):  # 枚举第 i 种字母选了 k 个
                    f[i][j] += f[i - 1][j - k] * comb(j, k)  # comb 也可以预处理,见其它语言
        return sum(f[m][1:])

python3 解法, 执行用时: 400 ms, 内存消耗: 13.5 MB, 提交时间: 2020-11-18 10:29:27

class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        self.res = -1
        dic = collections.Counter(tiles)

        def _backtrack(dic, path):
            for i in path:
                if path.count(i) > dic[i]:
                    return
            self.res += 1
            for i in dic:
                _backtrack(dic, path+i)
        
        _backtrack(dic, "")
        return self.res
        
            

上一题