1. 程式人生 > >演算法練習week3--leecode792

演算法練習week3--leecode792

題目描述

這道題的題目名稱非常的難懂,但是讀了題目內容以後,就不難理解了,定義函式f(x)為x!的末尾0的個數,現在給了我們一個非負整數K,問使得f(x)=K成立的非負整數的個數。

問題分析

之前做過一道有關階乘末尾零的個數的題,從那道里知道了末尾0其實是由2和5相乘為10得到的,而階乘中2的數量遠多於5的個數,所以10的個數就只取決於5的個數。

需要注意的一點就是,像25,125,這樣的不只含有一個5的數字需要考慮進去。比如,24的階乘末尾有4個0,分別是5,10,15,20中的四個5組成的,而25的階乘末尾就有6個0,分別是5,10,15,20中的各一個5,還有25中的兩個5,所以共有六個5,那麼就不存在其階乘數末尾有5個0的數。

除此之外,我們知道20,21,22,23,24,這五個數的階乘數末尾零的個數其實是相同的,都是有4個,因為它們包含的5的個數相同。而19,18,17,16,15,這五個數末尾零個數相同,均為3。那麼我們其實可以發現,每五個數,必會至少多出1個5,有可能更多。所以階乘末尾零個數均為K個的x值,只有兩種情況,要麼是5,要麼是0。

演算法設計

演算法1

基於之前那道題的解法,我們有了如何快速求一個給定的數字階乘末尾零的個數,那麼我們只要找到了一個這樣的數,其階乘末尾零的個數等於K的話,那麼就說明總共有5個這樣的數,返回5,反之,如果找不到這樣的數字,就返回0。那麼像這種選一個candidate數字,再用二分搜尋進行驗證的操作。首先要確定二分搜尋法的範圍,左邊界為0,關鍵是來確定右邊界。

我們來分析一下,一個數字的階乘末尾零個數為K,那麼這個數字能有多大,就拿前面舉的例子來說吧,末尾有4個0的最大數字是24,有六個0的最大是29,那麼我們發現它們都不會超過5*(K+1)這個範圍,所以這就是我們的右邊界。然後進行二分搜尋法。

注意:右邊界可能會超過整型數範圍,所以要用長整型來表示。

class Solution {
public:
    int preimageSizeFZF(int K) {
        long left = 0, right = 5L * (K + 1);
        while (left < right) {
            long mid = left + (right - left) / 2;
            long cnt = numOfTrailingZeros(mid);
            if (cnt == K) return 5;
            else if (cnt < K) left = mid + 1;
            else right = mid;
        }
        return 0;
    }
    long numOfTrailingZeros(long x) {
        long res = 0;
        for (; x > 0; x /= 5) {
            res += x / 5;
        }
        return res;
    }
};

演算法2

在演算法1 的基礎上進行程式碼模組整合,將while迴圈柔和進去即可,程式碼減少了30%。

class Solution {
public:
    int preimageSizeFZF(int K) {
        long left = 0, right = 5L * (K + 1);
        while (left < right) {
            long mid = left + (right - left) / 2, cnt = 0;
            for (long i = 5; mid / i > 0; i *= 5) {
                cnt += mid / i;
            }
            if (cnt == K) return 5;
            else if (cnt < K) left = mid + 1;
            else right = mid;
        }
        return 0;
    }
};

演算法3

通過後續的資料查詢,我發現有博主通過規律尋找的方法,找出了其中蘊含的規律,順利解決該問題。不過我以為,這樣的解法不具有演算法設計的巧妙性,有一點運氣的程式在裡面。所以,我在這裡不做贅述,截圖供參考,豐富思維,同時,我也按照他的思路實現了他的演算法。

class Solution {
public:
    int preimageSizeFZF(int K) {
        if (K < 5) return 5;
        int base = 1;
        while (base * 5 + 1 <= K) {
            base = base * 5 + 1;
        }
        if (K / base == 5) return 0;
        return preimageSizeFZF(K % base);
    }
};