原题再现
给你一个整数 n,请返回长度为 n 、仅由元音 (a, e, i, o, u) 组成且按 字典序排列 的字符串数量。
字符串 s 按 字典序排列 需要满足:对于所有有效的 i,s[i] 在字母表中的位置总是与 s[i+1] 相同或在 s[i+1] 之前
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/count-sorted-vowel-strings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例
解题思路
通过 hash 的方式
定义一个 hash,由于n始终大于等于1,所以可以默认'a'、'e'、'i'、'o'、'u' 初始值为1
当 n 增加一个长度,对于已有的所有组合结果:
以 a、e、i、o、u 开头的,都可以再以 a 开头
以 e、i、o、u 开头的,都可以在以 e 开头
......
func countVowelStrings(n int) (count int) {
hash := make(map[rune]int)
hash['a'], hash['e'], hash['i'], hash['o'], hash['u'] = 1, 1, 1, 1, 1
for i := 1; i < n; i++ {
hash['a'] += hash['e'] + hash['i'] + hash['o'] + hash['u']
hash['e'] += hash['i'] + hash['o'] + hash['u']
hash['i'] += hash['o'] + hash['u']
hash['o'] += hash['u']
}
for _, v := range hash {
count += v
}
return count
}
此算法的时间复杂度为O(n)
func countVowelStrings(n int) int {
a, e, i, o, u := 1, 1, 1, 1, 1
for m := 1; m < n; m++ {
e += a
i += e
o += i
u += o
}
return a + e + i + o + u
}
盒子模型(数学)
本方法借鉴自一位大佬
长度为5的元音数组组合成长度为n的字符串种数转换为数学问题就等价于有n个小球放到5个盒子里(盒子可以为空),有多少方式?
首先我解释一下为何能如此等价,因为题目条件的限制使得每一种确定的放置方式能且仅能得到一种组合,比如一个’a’一个’e’两个’o’,它只能得到一个结果,那就是’aeoo’。
然后我们来解决数学问题,这是一道高中的经典排列组合问题(组合数学里面也有),先看它的一般问题:
n个小球放到m个盒子里(盒子可以为空),有多少方式?
由于盒子可以为空,貌似不容易解决,我们可以尝试将其进行等价变形处理:
n+m个小球放到m个盒子里(每个盒子至少有一个小球),有多少方式?
第二种方式其实就是增加m个小球然后给每个盒子都默认放置一个小球即可。
那么如何处理第二种问题呢?接下来就要引入数学的神奇手法–“挡板”,我尝试用另外一种更容易理解的方式来描述这个“挡板”。
m个盒子有m-1个间隙,n+m个小球含有n+m-1个间隙,将n+m个小球放到m个盒子相当于在小球的n+m-1个间隙中选取m-1个间隙来放置挡板,进而将小球分成m个部分也就是m个盒子,那么总共就有C(n + m - 1, m - 1)种方式。回到本题,那么答案就是C(n + 4, 4)。
所以使用此种方式,那么只需要一行代码:
func countVowelStrings(n int) (count int) {
return (n + 4) * (n + 3) * (n + 2) * (n + 1) / 24
}
很明显,此算法的时间复杂度为O(1)
结论
- 数学是算法之基!
- 一个程序可以没有算法(屎山),但一个好的程序不能没有算法!