力扣- -阶乘函数后K个零
力扣- -階乘函數后K個零
文章目錄
- 力扣- -階乘函數后K個零
- 一、172. 階乘后的零
- 二、分析
- 三、代碼
- 四、階乘函數后K個零
- 五、分析
- 六、完整代碼
一、172. 階乘后的零
二、分析
- 求n!中末尾0的個數:
- 0的來源 2 * 5 ,所以一對2和5即可產生一個0,所以0的個數即為min(階乘中5的個數和2的個數)
- 又因為是2的倍數的數一定比是5的倍數的數多 所以2的個數一定>=5的個數 所以只需要統計 5 的個數了
-
例如 5! = 1 * 2 * 3 * 4 * 5
-
會產生3個2和1個5
-
一個2和一個5配對出現0 ,所以5!末尾只有一個零
-
而在 n = 25 時 可以產生5的有 5 10 15 20 25
-
即 n/5 = 5個 然而 25 = 5*5 所以少算了一個5
-
n>=25時,故而需要補上它 因此所有可以產生25的也要加上
-
即為 n/5 + n/25 然而 125 = 5*25 所以又少算了一個5
-
n>=125時,故而需要補上它。 因此所有可以產生125的也要加上
-
即為 n/5 + n/25 + n/125 然鵝 625 = 5*125 所以又少算了一個5
-
繼續補上…
-
所以最終為 n/5 + n/25 + n/125 + n/625 + …
-
即 n/5 + n/5/5 + n/5/5/5 + n/5/5/5/5 + ...
三、代碼
class Solution { public:int trailingZeroes(int n) {int five = 0;while(n >= 5){five += n / 5;n /= 5;}return five;} };四、階乘函數后K個零
五、分析
-
現在是給你一個非負整數K,問你有多少個n,使得n!結果末尾有K個 0。
-
一個直觀地暴力解法就是窮舉唄,因為隨著n的增加,n!肯定是遞增的,trailingZeroes(n)肯定也是遞增的,偽碼邏輯如下:
-
這種做法效率比較低,對于這種具有單調性的函數,用 for 循環遍歷,可以用二分查找進行降維打擊
-
搜索有多少個n滿足trailingZeroes(n) == K,其實就是在問,滿足條件的n最小是多少,最大是多少,最大值和最小值一減,就可以算出來有多少個n滿足條件了。
-
那不就是二分查找「搜索左側邊界」和「搜索右側邊界」這兩個事兒嘛?
-
因為二分查找需要給一個搜索區間,也就是上界和下界,上述偽碼中n的下界顯然是 0,但上界是+inf,這個正無窮應該如何表示出來呢?
-
首先,數學上的正無窮肯定是無法編程表示出來的,我們一般的方法是用一個非常大的值,大到這個值一定不會被取到。
- 比如說 int 類型的最大值INT_MAX(2^31 - 1,大約 31 億),還不夠的話就 long類型的最大值LONG_MAX(2^63 - 1,這個值就大到離譜了)。
-
那么我怎么知道需要多大才能「一定不會被取到」呢?這就需要認真讀題,看看題目給的數據范圍有多大。
-
這道題目實際上給了限制,K是在[0,10^9 ]區間內的整數,也就是說,trailingZeroes(n)的結果最多可能達到10^9。
-
然后我們可以反推,當trailingZeroes(n)結果為10^9時,n為多少?這個不需要你精確計算出來,你只要找到一個數hi,使得trailingZeroes(hi)比10^9大,就可以把hi當做正無窮,作為搜索區間的上界。
-
剛才說了,trailingZeroes函數是單調函數,那我們就可以猜,先算一下trailingZeroes(INT_MAX)的結果,比109小一些,那再用LONG_MAX算一下,遠超109了,所以LONG_MAX可以作為搜索的上界。
注意為了避免整型溢出的問題,trailingZeroes函數需要把所有數據類型改成 long:
// 邏輯不變,數據類型全部改成 long long trailingZeroes(long n) {long res = 0;for (long d = n; d / 5 > 0; d = d / 5) {res += d / 5;}return res; }現在就明確了問題:
- n屬于區間[0,LONG_MAX],我們要尋找滿足trailingZeroes(n) == K的左側邊界和右側邊界。
六、完整代碼
class Solution { public:/* 主函數 */int preimageSizeFZF(int K) {// 左邊界和右邊界之差 + 1 就是答案return right_bound(K) - left_bound(K) + 1;}/* 搜索 trailingZeroes(n) == K 的左側邊界 */long left_bound(int target) {long lo = 0, hi = LONG_MAX;while (lo < hi) {long mid = lo + (hi - lo) / 2;if (trailingZeroes(mid) < target) {lo = mid + 1;} else if (trailingZeroes(mid) > target) {hi = mid;} else {hi = mid;}}return lo;}/* 搜索 trailingZeroes(n) == K 的右側邊界 */long right_bound(int target) {long lo = 0, hi = LONG_MAX;while (lo < hi) {long mid = lo + (hi - lo) / 2;if (trailingZeroes(mid) < target) {lo = mid + 1;} else if (trailingZeroes(mid) > target) {hi = mid;} else {lo = mid + 1;}}return lo - 1;}long trailingZeroes(long n) {long res = 0;for (long d = n; d / 5 > 0; d = d / 5) {res += d / 5;}return res;} };總結
以上是生活随笔為你收集整理的力扣- -阶乘函数后K个零的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 力扣- -正则表达式匹配
- 下一篇: 力扣- - 最短回文串(KMP算法)