[LeetCode] 788. Rotated Digits 旋轉數字


 

X is a good number if after rotating each digit individually by 180 degrees, we get a valid number that is different from X.  Each digit must be rotated - we cannot choose to leave it alone.

A number is valid if each digit remains a digit after rotation. 0, 1, and 8 rotate to themselves; 2 and 5 rotate to each other; 6 and 9 rotate to each other, and the rest of the numbers do not rotate to any other number and become invalid.

Now given a positive number N, how many numbers X from 1 to N are good?

Example:
Input: 10
Output: 4
Explanation: 
There are four good numbers in the range [1, 10] : 2, 5, 6, 9.
Note that 1 and 10 are not good numbers, since they remain unchanged after rotating.

Note:

  • N  will be in range [1, 10000].

 

這道題定義了一種好數字,就是把每位上的數字自身翻轉一下,能得到一個不同的數字。翻轉規則定義為,0,1,和8翻轉后還為其本身,2和5,6和9可以互相翻轉。然后給了一個數字N,問 [1, N] 區間內共有多少個這樣的好數字。這道題大家踩👎的個數遠超頂👍的個數,貌似很多人不太喜歡這道給數字發好人卡的題,博主倒覺得這道題還不錯,感覺沒有太多的技巧,就是一個數字一個數字的驗證唄,對於每個數字,再一位一位的驗證唄。將驗證某個數字是否 Good 的操作寫到一個子函數中,遍歷數字的每一位的方法,可以通過不停的除以 10 來獲得,也可以像博主這樣通過轉變為字符串,再遍歷字符即可。翻轉規則中沒有提到的數字有三個,3,4,和7,說明這三個數字無法翻轉,若一旦被翻轉,則無法形成 valid 的數字,所以只要一旦遇到這三個數字中的一個,直接返回 false 即可。還有要注意的是,0,1,和8這三個數字翻轉后還是其本身,由於好數字的定義是需要翻轉一個不同的數字,所以若都是由這三個數字組成,翻轉后不會產生不同的數字,也不符合題意。所以需要2,5,6,和9這四個數字中至少出現一個,用一個 flag 來標記出現過這些數字,最后只要 check 這個 flag 變量即可,參見代碼如下:

 

解法一:

class Solution {
public:
    int rotatedDigits(int N) {
        int res = 0;
        for (int i = 1; i <= N; ++i) {
            if (check(i)) ++res;
        }
        return res;
    }
    bool check(int k) {
        string str = to_string(k);
        bool flag = false;
        for (char c : str) {
            if (c == '3' || c == '4' || c == '7') return false;
            if (c == '2' || c == '5' || c == '6' || c == '9') flag = true;;
        }
        return flag;
    }
};

 

其實這道題還有更好的解法呢,使用動態規划 Dynamic Programming 來做的,思路非常巧妙,博主深為嘆服。定義了一個長度為 N+1 的一維布爾型 DP 數組,其中 dp[i] 表示數字i的三種狀態,0表示數字i翻轉后不合法,1表示數字i翻轉后和原數相同,2表示數字i翻轉后形成一個不同的數字。那么根據題目中的定義可知,只有當 dp[i]=2 的時候才是好數字。那么下面來看狀態轉移方程吧,如果數字只有1位的話,那么判斷起來很簡單,如果是 0,1,和8中的一個,則 dp[i]=1,如果是 2,5,6,和9中的一個,則 dp[i]=2,並且結果 res 自增1。如果是剩下的三個數字 3,4,7中的一個不用更新,因為dp數組初始化就為0。下面來看數字i大於 10 的情況,非常的經典,dp[i] 的值其實可以從 dp[i/10] 和 dp[i%10] 這兩個狀態值轉移而來,由於更新的順序是從小到大,所以當要更新 dp[i] 的時候,dp[i/10] 和 dp[i%10] 這兩個狀態值已經算過了。為啥 dp[i] 的值是由這兩個狀態值決定的呢?因為每個數字都是相互獨立的翻轉,比如四位數字 abcd,可以拆分為三位數 abc,和個位數d,如果 abc 翻轉后仍是 abc,d翻轉后仍是d,說明 abcd 翻轉后仍是 abcd,所以 dp[i]=1,只要其中有一個大於1了,另外一個至少是1的話,那么說明可以翻轉成不同的數字,dp[i]=2,並且結果 res 自增1,參見代碼如下:

 

解法二:

class Solution {
public:
    int rotatedDigits(int N) {
        int res = 0;
        vector<int> dp(N + 1);   
        for (int i = 0; i <= N; ++i) {
            if (i < 10) {
                if (i == 0 || i == 1 || i == 8) dp[i] = 1;
                else if (i == 2 || i == 5 || i == 6 || i == 9) {
                    dp[i] = 2; ++res;
                }
            } else {
                int a = dp[i / 10], b = dp[i % 10];
                if (a == 1 && b == 1) dp[i] = 1;
                else if (a >= 1 && b >= 1) {
                    dp[i] = 2; ++res;
                }
            }
        }
        return res;
    }
};

 

討論:這題貌似還有 O(lgN) 的解法,比如這個帖子,但感覺比較 tricky,博主不是很理解,哪位看官大神們可以給博主講講~

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/788

 

參考資料:

https://leetcode.com/problems/rotated-digits/

https://leetcode.com/problems/rotated-digits/discuss/117975/Java-dp-solution-9ms

https://leetcode.com/problems/rotated-digits/discuss/264282/Java-O(logN)-0ms-100

https://leetcode.com/problems/rotated-digits/discuss/116530/O(logN)-Time-O(1)-Space

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM