星期一的困难,不止是不想上班……
1、看题
不要被困难吓倒,实际上这题并不多麻烦!
2、审题
题目倒是内容不多,不过还是有几点需要注意一下:
- 字符串仅能使用给出的5个元音字母
- 每个元音字母都有自己后续的限制
- 总数很大,需要对109+7取模
通常来说,遇到需要取模的大数结果,都意味有一定的难度。毕竟你需要考虑的情况和数量级有异常大的要求。
但这题真的很一般。
3、思路
首先明确一点,我们仅需要返回能够组成的不同字符串的数量。
题目并不要求我们列出全部(毕竟这个数量级也不好列)。
而组成字符串的元素仅有5个元音字母,各个元音字母的后续都有各自的规则。
于是你会发现,这5个元音字母就组成了一个简单的状态机。
接着,我们再根据示例进行后续的分析。
当n=1
时,结果为5,即 “a”,“e”, “i” , “o” 和 “u"五个字母。
当n=2
时,结果为10,详细可以查看示例2。
分析结果后会发现,由于字母’a’的规则为每个元音 ‘a’ 后面都只能跟着 ‘e’,于是在n=2
时, ‘a’ 的后续只能组成 “ae”。
同理,字母’e’的规则为每个元音 ‘e’ 后面只能跟着 ‘a’ 或者是 ‘i’,于是n=2
时有"ea"和"ei”。
于是你会发现,每次延伸字符串的长度时,所有尾缀为’a’的字符串可以延伸出1个,'e’的可以延伸出2个。同理,'i’为4个,'o’为2个,'u’为1个。
以此我们就能知道每次延伸字符串时的数量变化。
但是不行。
仅记录数量可不行,毕竟字符串还可能会继续延伸。我们得知道当字符串长度为n时,以"a", “e”, “i” , “o” 和 "u"结尾的字符串各有多少个,才能统计n+1时的数量。
再结合上面的规则就会发现:
f(a,n) = f(e,n-1) + f(i,n-1) + f(u,n-1)
同理,还可列出其他四个,如下:
f(e,n) = f(a,n-1) + f(i,n-1)
f(i,n) = f(e,n-1) + f(o,n-1)
f(o,n) = f(i,n-1)
f(u,n) = f(i,n-1) + f(o,n-1)
好吧,到这里方案已经呼之欲出了。
最后,记得还要取模哦。
4、开工!
class Solution {
private static final int MOD = 1000000007;
public int countVowelPermutation(int n) {
//current[0~4]表示当前长度时最后一位a、e、i、o、u的数量
long[] current = new long[]{1, 1, 1, 1, 1};
long[] next;
while (n > 1) {
next = new long[5];
next[0] = (current[1] + current[2] + current[4]) % MOD;
next[1] = (current[0] + current[2]) % MOD;
next[2] = (current[1] + current[3]) % MOD;
next[3] = current[2] % MOD;
next[4] = (current[2] + current[3]) % MOD;
current = next;
n--;
}
//计算总长度
long sum = 0;
for (int i = 0; i < 5; i++) {
sum += current[i];
}
return (int) (sum % MOD);
}
}
思路什么的应该很清晰了,后面就不再分析。
5、提交
6、咀嚼
嗯……
时间复杂度为O(CN),其中C=5
。
空间复杂度也为O(CN),毕竟我每次循环时都新建了一个长度为5的数组。
而实际上这部可以省略,仅使用到两个长度为5的数组就行,但代价是每次循环完后,要拷贝一次,会浪费些许时间。
7、康康大牛!
很明显,困难题加上不上不下的耗时排名,这题必有黑科技。
于是刷完后我也是光速冲到了题解里。
然后就看到了矩阵快速幂这么个东西。
相信各位在大学时都学过一个叫 《高等数学》 的课程。
而里面必然讲到过矩阵乘法这么个东西。
个人认为矩阵相关的内容相较其他高数内容来说还是十分简单的毕竟是少数连我都听懂了的 ,但奈何生活中实在很少应用到。于是乎,我又华丽丽地忘得一干二净了。
好吧,贴出三叶大佬的题解,供大家学习学习。
8、总结
今天的困难题属于较为简单的辣种了。
你会发现,我这种解法并不难想,更不难以实现。而这题锦上添花的矩阵快速幂做法也绝非什么高攀不起的做法,毕竟思路都是一致的,只是实现上有根本性的 区别。
但总之,能找个时间再好好回顾曾经学过的知识,还是挺不错的。
找个时间再回想一下被高数支配的恐惧