每日算法系列【LeetCode 233】數(shù)字 1 的個數(shù)

題目描述

給定一個整數(shù) n舶得,計算所有小于等于 n 的非負整數(shù)中數(shù)字 1 出現(xiàn)的個數(shù)伸但。

示例1

輸入:
13
輸出:
6
解釋:
數(shù)字 1 出現(xiàn)在以下數(shù)字中: 1, 10, 11, 12, 13 。

題解

這題是我搜數(shù)位 dp 題目搜出來的元旬,于是我直接用數(shù)位 dp 方法把它過了赃绊,后來發(fā)現(xiàn)其實沒必要這么麻煩既峡,簡單的計算就能算出來了,這里兩個方法我都講一下碧查。

數(shù)學方法

我們不妨用 n = 12345 來舉個例子运敢。要求小于等于 n 的數(shù)字里有多少個 1 ,我們不妨轉(zhuǎn)換個角度忠售,看某一位數(shù)字是 1 的話传惠,有多少數(shù)字小于 n 。

例如從右向左數(shù)第 i = 2 位(數(shù)字 3 )稻扬,如果這一位取 1 卦方,那么左邊 2 位如果取 0~11 ,那么右邊 2 位就沒有任何限制泰佳,從 0 取到 99 都行盼砍。如果左邊 2 位如果取 12 尘吗,那么就得考慮 n 中第 i 位是幾了,如果大于 1 浇坐,那么右邊 2 位還是沒有限制摇予;如果等于 1 ,那么右邊 2 位只能取 0~45 吗跋;如果等于 0 ,那就沒得取了宁昭。

下面這張圖是我打的草稿跌宛,看的更清楚一點:

image

一般化描述就是,考慮從右往左數(shù)第 i 位是 1 的數(shù)字數(shù)量积仗。那么 n 中第 i 位左邊部分的數(shù)字是 \left\lfloor \frac{n}{10^{i+1}} \right\rfloor 疆拘,而右邊可以取的數(shù)量是 10^i ,相乘就是總的數(shù)量 \left\lfloor \frac{n}{10^{i+1}} \right\rfloor \cdot 10^i 寂曹。如果左邊直接取最大值哎迄,那么就要考慮第 i 位數(shù)字是幾了,計算可以得到第 i 位數(shù)字為 \left\lfloor \frac{n}{10^{i}} \right\rfloor \% 10 隆圆,記為 x 漱挚。如果 x > 1 ,那么右邊無限制渺氧,有 10^i 種取法旨涝;如果 x = 1 ,那么右邊有 n \% 10^i + 1 種取法侣背;如果 x = 0 白华,那么右邊無法取,因為第 i 位都沒法取 1 贩耐。

綜上弧腥,令 x = \left\lfloor \frac{n}{10^{i}} \right\rfloor \% 10 ,那么答案就是:
\left\lfloor \frac{n}{10^{i+1}} \right\rfloor \cdot 10^i + 10^i \cdot [x > 1] + (n \% 10^i + 1) \cdot [x = 1]

數(shù)位dp

數(shù)位 dp 就麻煩許多了潮太,不想看的可以直接跳過了管搪。

首先我們從最高位開始往右遞歸計算,用 pos, count, limit 來表示計算到第 pos 位(從左往右消别,和數(shù)學方法不一樣)時抛蚤,已經(jīng)出現(xiàn)了 count 個 1 ,并且之后的數(shù)字有無限制(也就是能否取遍 0~9 )寻狂,這種狀態(tài)之下方法數(shù)是多少岁经。

那么第 pos 位我們可以取的數(shù)字有哪些呢?如果 limit = 1 也就是有限制蛇券,那么只能取 0~n中第pos位缀壤,如果沒有限制那就取 0~9 樊拓。

假設(shè)第 pos 位取 1 ,那么 pos 就轉(zhuǎn)移到了 pos+1 塘慕,count 轉(zhuǎn)移到了 count+1 筋夏,limit 呢?只有當原來有限制图呢,并且第 pos 位正好取了最大值也就是 n 中第 pos 位數(shù)字時条篷,limit 還是 1 ,否則的話限制取消蛤织,后面的數(shù)字隨便取赴叹。如果第 pos 位不取 1 ,那么除了 count 不變以外指蚜,其他兩個狀態(tài)還是跟上面一樣轉(zhuǎn)移乞巧。

終止狀態(tài)的話,如果遍歷到了最后一位結(jié)束摊鸡,就返回 count 數(shù)量就行了绽媒,表示當前數(shù)字中有 count 個 1 。

這樣的話會有很多重復(fù)計算的狀態(tài)免猾,所以需要用到記憶化搜索是辕,用 dp[pos][count] 來保存 pos, count, limit=0 狀態(tài)下的答案。為什么只保存 limit=0 的答案呢掸刊?因為只有無限制的情況下免糕,后面的數(shù)字才能隨便取,跟 n 是多少沒有關(guān)系忧侧。否則的話 n 變了后面的值就會受限于 n 石窑,那么就不是一個定值了,沒法保存蚓炬。

那么 limit=1 不保存的話會不會超時呢松逊?不會的,因為每一位只有一種取法會使得后面的數(shù)字繼續(xù)有限制肯夏,所以整體上來看经宏,有限制的狀態(tài)個數(shù)是個常數(shù),并不需要擔心超時驯击。

代碼

數(shù)學方法(c++)

class Solution {
public:
    int countDigitOne(int n) {
        int res = 0;
        for (long i = 1; i <= n; i *= 10) {
            res += n / (i * 10) * i;
            int x = (n / i) % 10;
            res += x > 1 ? i : (n % i + 1) * x;
        }
        return res;
    }
};

數(shù)位dp(c++)

class Solution {
public:
    int a[14];
    int dp[14][14];
    
    int dfs(int pos, int count, int limit) {
        if (!pos) return count;
        if (!limit && dp[pos][count] != -1) return dp[pos][count];
        int res = 0, ub = limit ? a[pos] : 9;
        for (int i = 0; i <= ub; ++i) {
            res += dfs(pos-1, count+(i==1), limit&&i==a[pos]);
        }
        return limit ? res : dp[pos][count]=res;
    }
    
    int countDigitOne(int n) {
        memset(dp, -1, sizeof dp);
        int len = 0;
        while (n) {
            a[++len] = n % 10;
            n /= 10;
        }
        return dfs(len, 0, 1);
    }
};

數(shù)學方法(python)

class Solution:
    def countDigitOne(self, n: int) -> int:
        res, i = 0, 1
        while i <= n:
            res += n // (i * 10) * i
            x = (n // i) % 10
            res += i if x > 1 else (n % i + 1) * x
            i *= 10
        return res
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烁兰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子徊都,更是在濱河造成了極大的恐慌沪斟,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暇矫,死亡現(xiàn)場離奇詭異主之,居然都是意外死亡择吊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門槽奕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來几睛,“玉大人,你說我怎么就攤上這事粤攒∷” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵夯接,是天一觀的道長必峰。 經(jīng)常有香客問我,道長钻蹬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任凭需,我火速辦了婚禮问欠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粒蜈。我一直安慰自己顺献,他們只是感情好,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布枯怖。 她就那樣靜靜地躺著注整,像睡著了一般。 火紅的嫁衣襯著肌膚如雪度硝。 梳的紋絲不亂的頭發(fā)上肿轨,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音蕊程,去河邊找鬼椒袍。 笑死,一個胖子當著我的面吹牛藻茂,可吹牛的內(nèi)容都是我干的驹暑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼辨赐,長吁一口氣:“原來是場噩夢啊……” “哼优俘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掀序,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤帆焕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后森枪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體视搏,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡审孽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浑娜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佑力。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筋遭,靈堂內(nèi)的尸體忽然破棺而出打颤,到底是詐尸還是另有隱情,我是刑警寧澤漓滔,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布编饺,位于F島的核電站,受9級特大地震影響响驴,放射性物質(zhì)發(fā)生泄漏透且。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一豁鲤、第九天 我趴在偏房一處隱蔽的房頂上張望秽誊。 院中可真熱鬧,春花似錦琳骡、人聲如沸锅论。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽最易。三九已至,卻和暖如春炫狱,著一層夾襖步出監(jiān)牢的瞬間藻懒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工视译, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留束析,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓憎亚,卻偏偏與公主長得像员寇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子第美,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容