LCP 25. 古董键盘

题目描述

小扣在秋日市集购买了一个古董键盘。由于古董键盘年久失修,键盘上只有 26 个字母 a~z 可以按下,且每个字母最多仅能被按 k 次。

小扣随机按了 n 次按键,请返回小扣总共有可能按出多少种内容。由于数字较大,最终答案需要对 1000000007 (1e9 + 7) 取模。

示例 1:

输入:k = 1, n = 1

输出:26

解释:由于只能按一次按键,所有可能的字符串为 "a", "b", ... "z"

示例 2:

输入:k = 1, n = 2

输出:650

解释:由于只能按两次按键,且每个键最多只能按一次,所有可能的字符串(按字典序排序)为 "ab", "ac", ... "zy"

提示:

  • 1 <= k <= 5

  • 1 <= n <= 26*k

解法

方法一:动态规划

我们定义 $f[i][j]$ 表示按了 $i$ 次按键,且使用了前 $j$ 个字母的方案数。初始时 $f[0]$ 全部为 $1$,表示没有按键时只有一种方案,其余 $f[i][j] = 0$。

对于 $f[i][j]$,我们考虑其转移方式。我们可以枚举第 $j$ 个字母一共使用了多少次,设为 $h$,其中 $0 \leq h \leq \min(i, k)$,那么我们可以从 $f[i - h][j - 1]$ 转移过来,且第 $j$ 个字母可以在 $i$ 次按键中使用 $h$ 次,方案数为 $\binom{i}{h}$。即:

$$ f[i][j] = \sum_{h = 0}^{\min(i, k)} f[i - h][j - 1] \cdot \binom{i}{h} $$

最终答案即为 $f[n][26]$。

时间复杂度 $O(n \times k \times |\Sigma|)$,空间复杂度 $O(n \times |\Sigma|)$。其中 $|\Sigma|$ 表示字母表大小。

Python3

class Solution:
    def keyboard(self, k: int, n: int) -> int:
        f = [[0] * 27 for _ in range(n + 1)]
        f[0] = [1] * 27
        mod = 10**9 + 7
        for i in range(1, n + 1):
            for j in range(1, 27):
                for h in range(min(k, i) + 1):
                    f[i][j] += f[i - h][j - 1] * comb(i, h)
                    f[i][j] %= mod
        return f[n][26]

Java

class Solution {
    public int keyboard(int k, int n) {
        int[][] c = new int[n + 1][k + 1];
        for (int i = 0; i <= n; ++i) {
            c[i][0] = 1;
        }
        final int mod = (int) 1e9 + 7;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= k; ++j) {
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
            }
        }
        int[][] f = new int[n + 1][27];
        Arrays.fill(f[0], 1);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j < 27; ++j) {
                for (int h = 0; h <= Math.min(i, k); ++h) {
                    f[i][j] = (f[i][j] + (int) ((long) f[i - h][j - 1] * c[i][h] % mod)) % mod;
                }
            }
        }
        return f[n][26];
    }
}

C++

class Solution {
public:
    int keyboard(int k, int n) {
        int f[n + 1][27];
        memset(f, 0, sizeof(f));
        for (int j = 0; j < 27; ++j) {
            f[0][j] = 1;
        }
        int c[n + 1][k + 1];
        memset(c, 0, sizeof(c));
        c[0][0] = 1;
        const int mod = 1e9 + 7;
        for (int i = 1; i <= n; ++i) {
            c[i][0] = 1;
            for (int j = 1; j <= k; ++j) {
                c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
            }
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j < 27; ++j) {
                for (int h = 0; h <= min(i, k); ++h) {
                    f[i][j] = (f[i][j] + 1LL * f[i - h][j - 1] * c[i][h] % mod) % mod;
                }
            }
        }
        return f[n][26];
    }
};

Go

func keyboard(k int, n int) int {
	c := make([][]int, n+1)
	for i := range c {
		c[i] = make([]int, k+1)
		c[i][0] = 1
	}
	const mod int = 1e9 + 7
	for i := 1; i <= n; i++ {
		for j := 1; j <= k; j++ {
			c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod
		}
	}
	f := make([][27]int, n+1)
	for j := range f[0] {
		f[0][j] = 1
	}
	for i := 1; i <= n; i++ {
		for j := 1; j < 27; j++ {
			for h := 0; h <= min(i, k); h++ {
				f[i][j] = (f[i][j] + f[i-h][j-1]*c[i][h]%mod) % mod
			}
		}
	}
	return f[n][26]
}

TypeScript

function keyboard(k: number, n: number): number {
    const c: number[][] = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0));
    c[0][0] = 1;
    const mod = 10 ** 9 + 7;
    for (let i = 1; i <= n; ++i) {
        c[i][0] = 1;
        for (let j = 1; j <= k; ++j) {
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
        }
    }
    const f: number[][] = Array.from({ length: n + 1 }, () => Array(27).fill(0));
    f[0].fill(1);
    for (let i = 1; i <= n; ++i) {
        for (let j = 1; j < 27; ++j) {
            for (let h = 0; h <= Math.min(i, k); ++h) {
                const v = Number((BigInt(f[i - h][j - 1]) * BigInt(c[i][h])) % BigInt(mod));
                f[i][j] = (f[i][j] + v) % mod;
            }
        }
    }
    return f[n][26];
}

Swift

class Solution {
    func keyboard(_ k: Int, _ n: Int) -> Int {
        let mod = 1_000_000_007
        var c = Array(repeating: Array(repeating: 0, count: k + 1), count: n + 1)
        for i in 0...n {
            c[i][0] = 1
        }

        for i in 1...n {
            for j in 1...k {
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod
            }
        }

        var f = Array(repeating: Array(repeating: 0, count: 27), count: n + 1)
        for j in 0..<27 {
            f[0][j] = 1
        }

        for i in 1...n {
            for j in 1..<27 {
                for h in 0...min(i, k) {
                    f[i][j] = (f[i][j] + (f[i - h][j - 1] * c[i][h]) % mod) % mod
                }
            }
        }

        return f[n][26]
    }
}