问题描述
已知有一个长度为 26 的整型数组, 分别代表 26 个字母的个数, 问这些字母能组成多少种不同的字符串(取模 1000000007).
问题求解
纯暴力
开始觉得很简单, 纯迭代问题, 抬手就来.
int calc(int[] nums) {
int ret = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
nums[i]--;
ret += calc(nums);
ret %= 1000000007;
nums[i]++;
}
}
return ret > 0 ? ret : 1;
}
结果超时, 不行, 看来只能用数学方法求解.
数学方法
$$ Sum=\dfrac{A_{字母总数}^{字母总数}}{A_{A的数量}^{A的数量}\times……\times A_{2}^{2}} $$
题目没有讲太清楚(是否这些字母一定要全部使用), 这里为了简单就假设全部使用.
解法一:排列组合.
生成字符串长度是 $sum=a_0+a_1+a_2+…+a_{25}$.
放 $a$ , 有$(sum, a0)$种选择的方法; 放$ b$, 有$(sum - a0, a1)$种方法; 放 $c$, 有$(sum - a0 - a1, a2)$种方法…..直到最后
程序
private long solve1(int[] letters) {
int sum = 0;
for (int c : letters) {
sum += c;
}
BigInteger r = BigInteger.ONE;
for (int c : letters) {
BigInteger s = choose(sum, c);
r = r.multiply(s);
sum -= c;
}
return r.longValue();
}
private static BigInteger choose(int n, int k) {
BigInteger r = BigInteger.ONE;
for (int i=0; i<k; i++) {
r = r.multiply(BigInteger.valueOf(n - i));
}
for (int i=2; i<=k; i++) {
r = r.divide(BigInteger.valueOf(i));
}
return r;
}
解法 2: EGF (指数生成函数)。
这是个排列问题所以用指数生成函数.
对于$a$: 有$ a_0$ 个$ a$ 要放.写成指数函数形式 $e_0=e^{(a_0)/a_0!}$ .
同样对于 $b$ : 有 $a_1$ 个 $b$ 要放。写成指数函数形式 $e_1=e^{(a_1)/a_1!}$ 等等等等 最后到 $z$ , 有 $a_{25}$ 个 z 要放,写成指数函数形式 $e_{25}=e^{(a_{25})/a_5!} $根据指数生成函数的理论,整个排列数就是 $E=e_0e_1…*e_{25}$
中项 $e^{(sum)/sum!}$ 的系数.
// Solve by GEF
private long solve2(int[] a) {
int sum = 0;
for (int c : a) {
sum += c;
}
BigInteger p = p(sum);
BigInteger c = BigInteger.ONE;
for (int i : a) {
c = c.multiply(p(i));
}
return p.divide(c).longValue();
}
private BigInteger p(int sum) {
BigInteger r = BigInteger.ONE;
for (int i= sum; i>=2; i--) {
r = r.multiply(BigInteger.valueOf(i));
}
return r;
}
完毕.