2021-08-27:正常的里程表会依次显示自然数表示里程,吉祥的里程表会忽略含有4的数字而跳到下一个完全不含有4的数。正常:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 X。吉祥:1 2 3 5 6 7 8 9 10 11 12 13 15 16 17 ... 38 39 50 51 52 53 55。给定一个吉祥里程表的数字num(当然这个数字中不含有4)。返回这个数字代表的真实里程。

福大大 答案2021-08-27:

这道题的本质是一个9进制的数转成10进制的数。
0-3不变。5-9变成4-8。
比如39,先变成38,然后3*9+8=35。35就是需要的返回的值。
时间复杂度:O(lgN)。
空间复杂度:O(1)。

代码用golang编写。代码如下:

package main

import "fmt"

func main() {
    ret := notContains4Nums1(39)
    fmt.Println(ret)

    ret = notContains4Nums2(39)
    fmt.Println(ret)

    ret = notContains4Nums3(39)
    fmt.Println(ret)
}

// num中一定没有4这个数字
func notContains4Nums1(num int) int {
    count := 0
    for i := 1; i <= num; i++ {
        if isNot4(i) {
            count++
        }
    }
    return count
}

func isNot4(num int) bool {
    for num != 0 {
        if num%10 == 4 {
            return false
        }
        num /= 10
    }
    return true
}

// arr[1] : 比1位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?0个
// arr[2] : 比2位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?9个
// 1 -> 0 1 2 3 - 5 6 7 8 9 = 9
// arr[3] :比3位数还少1位,有几个数(数字里可以包含0但是不可以包含4)?81个
// 1 : 0 (0 1 2 3 - 5 6 7 8 9) = 9
// 2 :
// 1 (0 1 2 3 - 5 6 7 8 9) = 9
// 2 (0 1 2 3 - 5 6 7 8 9) = 9
// 3 (0 1 2 3 - 5 6 7 8 9) = 9
// 5 (0 1 2 3 - 5 6 7 8 9) = 9
// ...
// 9 (0 1 2 3 - 5 6 7 8 9) = 9
var arr []int = []int{0, 1, 9, 81, 729, 6561, 59049, 531441, 4782969, 43046721, 387420489,
    3486784401, 31381059609, 282429536481, 2541865828329, 22876792454961, 205891132094649,
    1853020188851841, 16677181699666569, 150094635296999121, 1350851717672992089}

// num中一定没有4这个数字
func notContains4Nums2(num int) int {
    if num <= 0 {
        return 0
    }
    // num的十进制位数,len
    len2 := decimalLength(num)
    // 35621
    // 10000
    offset := offset(len2)

    // 第一位数字
    first := num / offset
    return arr[len2] - 1 + (first-twoSelectOne(first < 4, 1, 2))*arr[len2] + process(num%offset, offset/10, len2-1)
}

// num之前,有开头!
// 在算0的情况下,num是第几个数字
// num 76217
// 10000
// 6217
// 1000
// len
func process(num int, offset int, len2 int) int {
    if len2 == 0 {
        return 1
    }
    first := num / offset
    return twoSelectOne(first < 4, first, (first-1))*arr[len2] + process(num%offset, offset/10, len2-1)
}

// num的十进制位数
// num = 7653210
// 7
func decimalLength(num int) int {
    len2 := 0
    for num != 0 {
        len2++
        num /= 10
    }
    return len2
}

// len = 6
// 100000
// len = 4
// 1000
// 3452168
// 1000000
// 3
func offset(len2 int) int {
    offset := 1
    for i := 1; i < len2; i++ {
        offset *= 10
    }
    return offset
}

// 讲完之后想到了课上同学的留言
// 突然意识到,这道题的本质是一个9进制的数转成10进制的数
// 不过好在课上的解法有实际意义,就是这种求解的方式,很多题目都这么弄
// 还有课上的时间复杂度和"9进制的数转成10进制的数"的做法,时间复杂度都是O(lg N)
// 不过"9进制的数转成10进制的数"毫无疑问是最优解
func notContains4Nums3(num int) int {
    if num <= 0 {
        return 0
    }
    ans := 0
    for base, cur := 1, 0; num != 0; num, base = num/10, base*9 {
        cur = num % 10
        ans += twoSelectOne(cur < 4, cur, cur-1) * base
    }
    return ans
}

func twoSelectOne(c bool, a int, b int) int {
    if c {
        return a
    } else {
        return b
    }
}

执行结果如下:
图片


左神java代码