[LeetCode] 1088. Confusing Number II 易混淆數之二



We can rotate digits by 180 degrees to form new digits. When 0, 1, 6, 8, 9 are rotated 180 degrees, they become 0, 1, 9, 8, 6 respectively. When 2, 3, 4, 5 and 7 are rotated 180 degrees, they become invalid.

confusing number is a number that when rotated 180 degrees becomes a different number with each digit valid.(Note that the rotated number can be greater than the original number.)

Given a positive integer N, return the number of confusing numbers between 1 and N inclusive.

Example 1:

Input: 20
Output: 6
Explanation:
The confusing numbers are [6,9,10,16,18,19].
6 converts to 9.
9 converts to 6.
10 converts to 01 which is just 1.
16 converts to 91.
18 converts to 81.
19 converts to 61.

Example 2:

Input: 100
Output: 19
Explanation:
The confusing numbers are [6,9,10,16,18,19,60,61,66,68,80,81,86,89,90,91,98,99,100].

Note:

  1. 1 <= N <= 10^9

這道題是之前那道 Confusing Number 的拓展,之前那道只是問一個給定的數字是否是迷惑數,而這道題是問給定數字N之內有多少個迷惑數字。這樣的話難度就增加了不少,肯定不要想着直接遍歷小於N的所有數字,對每個數字都調用之前的判斷方法,毫無疑問的會超時。實際上大多數字都不是迷惑數字,所以一個一個的檢驗非常的不高效。這里需要使用一些技巧,由於組成迷惑數的只有五個數字,那么迷惑數的每個位上只能是這五個數字,於是就可以用遞歸來遍歷所有的情況,假如N是個三位數,那么每一位有五種情況,總共也就 125 個數字要驗證,遠小於遍歷所有的數字。但是由於迷惑數要求翻轉后跟原數字不相同,所以還需要一個子函數判斷一下是否是真正的迷惑數,這個需要計算出翻轉后的數字,然后跟原數字比較一下,不相同才返回 true。當判斷了是真正的迷惑數時,結果 res 自增1即可,參見代碼如下:


解法一:

class Solution {
public:
    int confusingNumberII(int N) {
    	int res = 0;
        unordered_map<int, int> m{{0, 0}, {1, 1}, {6, 9}, {8, 8}, {9, 6}};
        helper(N, 0, m, res);
        return res;
    }
    void helper(int N, long cur, unordered_map<int, int>& m, int& res) {
    	if (isConfusingNum(cur, m)) ++res;
    	for (auto a : m) {
    		if (cur * 10 + a.first <= N && cur * 10 + a.first != 0) {
    			helper(N, cur * 10 + a.first, m, res);
    		}
    	}
    }
    bool isConfusingNum(long num, unordered_map<int, int>& m) {
    	long oldNum = num, res = 0;
    	while (num > 0) {
    		if (m.count(num % 10)) return false;
    		res = res * 10 + m[num % 10];
    		num /= 10;
    	}
    	return res != oldNum;
    }
};

我們還可以使用迭代的寫法,思路跟上面的遞歸寫法基本一樣,就是寫法上有些不同,這里用到一個數組 vec,初始時將0放入,然后進行循環,循環的條件是 found 為 false,這個 found 變量是用來控制當前生成的數字是否在 [1, N] 范圍中的。在循環中需要用一個臨時數組t,此時生成的方法是,遍歷 vec 數組中的每個數字,在其基礎上新增一位數字,這個數字要遍歷五個候選數字,組成新的數字后首先看是否越界,是的話將found 賦值為 true,然后直接 break 掉內層的 for 循環。因為這種 BFS 的遍歷方式是一種層序遍歷,之后組成的數字一定會越來越大,所以只要當前超過 N 了,后面的數字一定都會超過N。假如沒超過N,若當前數字不為0,則將其加入t數組中,然后再對該數字調用判斷迷惑數的子函數,若返回 true,則結果 res 自增1。內層 for 循環結束后,看下 found 值,若為 true,則 break 掉外層 for 循環。外層 for 循環結束后,要將臨時數組t賦值給數組 vec,參見代碼如下:


解法二:

class Solution {
public:
    int confusingNumberII(int N) {
    	int res = 0;
        map<int, int> m{{0, 0}, {1, 1}, {6, 9}, {8, 8}, {9, 6}};
        vector<int> vec{0};
        bool found = false;
        while (!found) {
        	vector<int> t;
        	for (int num : vec) {
        		for (auto a : m) {
        			int cur = num * 10 + a.first;
        			if (cur > N) {
        				found = true; break;
        			}
        			if (cur != 0) t.push_back(cur);
        			if (isConfusingNum(cur, m)) ++res;
        		}
        		if (found) break;
        	}
        	vec = t;
        }
        return res;
    }
    bool isConfusingNum(int num, map<int, int>& m) {
    	int oldNum = num, res = 0;
    	while (num > 0) {
    		if (!m.count(num % 10)) return false;
    		res = res * 10 + m[num % 10];
    		num /= 10;
    	}
    	return res != oldNum;
    }
};

Github 同步地址:

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


類似題目:

Confusing Number


參考資料:

https://leetcode.com/problems/confusing-number-ii/

https://leetcode.com/problems/confusing-number-ii/discuss/313407/Backtracking-and-Iterative-Solution-in-Java

https://leetcode.com/problems/confusing-number-ii/discuss/446589/Easy-to-understand-Java-backtracking-solution-covers-edge-case

https://leetcode.com/problems/confusing-number-ii/discuss/392786/Confusing-number-Total-number-Strobogrammatic-number-(Java-1ms)


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


免責聲明!

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



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