Leetcode 788:旋转数字(超详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:[email protected] https://blog.csdn.net/qq_17550379/article/details/88647103

我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。

如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方;6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。

现在我们有一个正整数 N, 计算从 1N 中有多少个数 X 是好数?

示例:
输入: 10
输出: 4
解释: 
在[1, 10]中有四个好数: 2, 5, 6, 9。
注意 1 和 10 不是好数, 因为他们在旋转之后不变。

注意:

  • N 的取值范围是 [1, 10000]

解题思路

我们首先可以想到一个比较笨的方法。我们可以对[1,N]的所有数都进行判断是不是好数。那现在问题就变成了怎么判断一个数是不是好数?首先这个数里面必须没有[3,4,7],在这个前提下我们的数字中必须包含[2,5,6,9]中的数。

class Solution:
    def rotatedDigits(self, N: int) -> int:
        s1 = set([3, 4, 7])
        s2 = set([2, 5, 6, 9])
        def isGood(n):
            res = False
            while n:
                t = n % 10
                if t in s1:
                    return False
                if t in s2:
                    res = True
                n //= 10
            return res
        
        ret = 0
        for i in range(1, N+1):
            if isGood(i):
                ret += 1
        return ret

其实你可以很清晰的认识到这种做法不足,我们有什么更好的做法呢?有,我们可以先思考一个三位数,也就是[0,999]有多少个好数呢?我们知道好数必须从[0,1,2,5,6,8,9]中选,所以就是7^3,但是这其中有一些不是,那就是只包含[0,1,8]的数,这些数有多少呢?3^3。所以我们最后的结果就是7^3-3^3=316。但是这种策略会碰到这样的问题?如果我们N=1234怎么办?我们显然不能先计算7^4了,因为这种情况下数的范围是[1,9999]。我们可以先考虑[1,999],再考虑[1000,1199],接着考虑[1200,1229][1230,1234]。好,现在问题变成了思考[1000,1199]怎么处理?显然我们的第二个数只有两种策略[0,1],而后面的两个数会有7种策略,也就是我们会有2*7^2,但是这其中会有2*3^2种是无效的,所以结果就是2*7^2-2*3^2=80,同理我们可以思考后面的两种范围,最后的结果就是316+80+21=417。这里需要注意关于21的计算,我们通过3*7得到,我们并没有减一个数因为我们之前碰到了2,而2非常特殊。

现在我们需要将上面这个策略整理为算法。我们首先需要将N切分成list。接着我们就要遍历N-list,如果遍历到的元素v==0,我们继续遍历下一个元素,如果不是0,我们就可以通过res += validRotations[t]*validRotations[9]**(n - i - 1)(其中t=v-1validRotations表示前t个元素中包含[0,1,8,2,5,6,9]的个数)。同时我们需要判断减去什么,此时就要分两种情况,如果之前碰到过[2,5,6,9]中的数话,我们就不用减任何数了,否则的话我们需要res -= sameRotations[t]*sameRotations[9]**(n - i - 1)

接着思考边界问题,当我们遍历到最后一个数的时候,我们只需res += validRotations[v],同时我们需要依照是否访问过[2,5,6,9]中元素的策略,考虑是不是需要res -= sameRotations[v]。如果我们遍历到了[3,4,7]中的元素,我们直接返回结果就可以了。

class Solution:
    def rotatedDigits(self, N: int) -> int:
        s1 = set([3, 4, 7])
        s2 = set([2, 5, 6, 9])
        validRotations = [1,2,3,3,3,4,5,5,6,7] # Cumulative: Valid number
        sameRotations = [1,2,2,2,2,2,2,2,3,3] # Cumulative: Same number
        res = 0
        N = list(map(int, str(N)))
        n = len(N)
        flag = True
        for i, v in enumerate(N): 
            if i == n-1:
                res += validRotations[v]
                if flag:
                    res -= sameRotations[v]
            else:
                if v == 0:
                    continue
                t = v - 1
                res += validRotations[t]*validRotations[9]**(n - i - 1)
                if flag:
                    res -= sameRotations[t]*sameRotations[9]**(n - i - 1)
            if v in s2:
                flag = False
            if v in s1:
                return res
            
        return res

一个更简洁的写法

class Solution:
    def rotatedDigits(self, N: int) -> int:
		s1 = set([0, 1, 8])
        s2 = set([0, 1, 8, 2, 5, 6, 9])
        s = set()
        res = 0
        N = map(int, str(N))
        for i, v in enumerate(N):
            for j in range(v):
                if s.issubset(s2) and j in s2:
                    res += 7**(len(N) - i - 1)
                if s.issubset(s1) and j in s1:
                    res -= 3**(len(N) - i - 1)
            if v not in s2:
                return res
            s.add(v)
        return res + (s.issubset(s2) and not s.issubset(s1))

reference:

http://www.frankmadrid.com/ALudicFallacy/2018/02/28/rotated-digits-leet-code-788/

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

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/88647103