剑指 Offer 46. 把数字翻译成字符串

贴一个答案:黑龙江大佬的:链接 分为字符串遍历 和 求余数 两个方法,分别是On,O1版本

  1. 方法1:字符串遍历
  2. func translateNum(num int) int {
  3. bytes := []byte(strconv.Itoa(num))
  4. n := len(bytes)
  5. arr := make([]int, n)
  6. for i := 0; i < n; i++ {
  7. arr[i] = int(bytes[i] - '0')
  8. }
  9. dp := make([]int, n+1)
  10. dp[0] = 1
  11. dp[1] = 1
  12. for i := 2; i <= n; i++ {
  13. if arr[i-2] != 0 && (arr[i-2]*10+arr[i-1]) < 26 {
  14. dp[i] = dp[i-1] + dp[i-2]
  15. } else {
  16. dp[i] = dp[i-1]
  17. }
  18. }
  19. return dp[n]
  20. }
方法2:数字求余
时间复杂度:On
空间复杂度:O1

func translateNum2(num int) int {
    a := 1
    b := 1
    x, y := num%10, num%10

    for num != 0 {
        num /= 10
        x = num % 10
        temp := 10*x + y
        c := a

        if 10 <= temp && temp <= 25 {
            c = a + b
        }
        b = a
        a = c
        y = x
    }
    return a
}

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
image.png
image.png

  • 暴力 DFS
    • time:O(2^n) 。每个节点都要遍历,栈的深度 n ,n 是数字字符个数
    • space: O(n) 。栈的深度 n ,n 是数字字符个数
  • DFS + 备忘录

    • time:O(n) 。栈的深度 n ,n 是数字字符个数
    • space: O(n) 。栈的深度 n ,n 是数字字符个数

      //自顶向下 == 递归 + 记忆化 ||  难点在处理字符串!
      func translateNum(num int) int {
      str := strconv.Itoa(num)        //变成“字符串1234”
      n := len(str)                   //字符串长度 4, 不是原数组长度1000
      
      dp := make([]int, n+1)
      dp[0], dp[1] = 1, 1            //因为dp【2】= dp【0】+ dp【1】=2 ,所以dp【0】= 1
      
      for i := 2; i <= n; i++ {
         dp[i] += dp[i-1]
         if str[i-2:i-1] == "0" {    //核心,判断条件,最优子结构 函数的分类
             continue
         }
         two_digit, _ := strconv.Atoi(str[i-2:i]) 
             if two_digit <= 25 {
                 dp[i] += dp[i-2]
             }
      }
      return dp[n]
      }
      
      //1,自顶向下 == 递归 + 记忆化  , 字符串的处理 稍有不同
      func translateNum(num int) int {
      tmp := num
      nums := make([]int,0)
      count := 0
      for tmp > 0{
         nums = append(nums,tmp%10)
         tmp = tmp/10
         count++
      }
      if count <= 1{
         return 1
      }
      dp := make([]int,count+1)
      dp[0] = 1
      dp[1] = 1
      for i:=2;i<=count;i++{
         tmp = nums[i-1]*10+nums[i-2]
         if tmp >= 10 && tmp <= 25{
             dp[i] = dp[i-1]+dp[i-2]
         }else{
             dp[i] = dp[i-1]
         }
      }
      return dp[count]
      }
      
//2,自顶向下 == 递归 + 记忆化  , 字符串的处理 稍有不同
func translateNum(num int) int {

    //此题如同跳楼梯 12258=abcde
    //如果de可以翻译,那么翻译方案是f(i-2),
    //否则是f(i-1)
    //可以递推公式: f(i) = f(i-1)+f(i-2)
    //否则递推公式: f(i) = f(i-1)

    s := fmt.Sprintf("%d", num)
    bs := []byte(s)
    matrix := make([]int, len(bs)+1)
    matrix[0] = 1
    matrix[1] = 1
    for i:=1;i<len(bs);i++{
        tmp := (bs[i-1]-'0') * 10 + (bs[i]-'0')
        if bs[i-1]!='0' && tmp<=25{
            //如果和前一个可以组成小于25的
            matrix[i+1] = matrix[i] + matrix[i-1]
        }else{
            matrix[i+1] = matrix[i]
        }
    }
    return matrix[len(matrix)-1]
}
  • 时间复杂度:循环的次数是 n 的位数,故渐进时间复杂度为 O(logn)。
  • 空间复杂度:虽然这里用了滚动数组,动态规划部分的空间代价是 O(1) 的,但是这里用了一个临时变量把数字转化成了字符串,故渐进空间复杂度也是 O(logn)
//自底向上,时On,空间O1;     但是有字符串处理,所以时空都变成logn。
//考点2:滚动数组优化空间!
func translateNum(num int) int {
    src := strconv.Itoa(num)
    p, q, res := 0, 0, 1

    for i := 0; i < len(src); i++ {
        p, q, res = q, res, 0           //dp【i】= dp【i-1】,res前进一位,p,q = dp[i-2],dp[i-1]
        res += q

        if i == 0 {
            continue
        }

        sum := src[i-1:i+1]   // 切片索引是左闭右开
        if sum <= "25" && sum >= "10" {   
            res += p  // 命中就加上前一次的,res = p+q
        }
    }
    return res
}