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.
A 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 <= 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
類似題目:
參考資料:
https://leetcode.com/problems/confusing-number-ii/
