leetcode:91. 解码方法

题目

一条包含字母 A-Z 的消息通过以下映射进行了 编码

  1. 'A' -> "1"
  2. 'B' -> "2"
  3. ...
  4. 'Z' -> "26"

解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:

  • "AAJF" ,将消息分组为 (1 1 10 6)
  • "KJF" ,将消息分组为 (11 10 6)

注意,消息不能分组为 (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6""06" 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数
题目数据保证答案肯定是一个 32 位 的整数。

示例:

输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。

解答 & 代码

解法一:动态规划

动态规划:

  • 动态规划数组 **dp**dp[i] 代表前 i 个字符 s[0,...,i-1] 的解码方式数量
  • 状态转移方程:**dp[i] = 0 + val1 + val2**
    • 若单个数字 s[i-1] 本身可以解码为一个字母,则 val1 = dp[i-1],否则 val1 = 0
    • 若两个数字 s[i-2] s[i-1] 可以解码为一个字母,则 val2 = dp[i-2],否则 val2 = 0
  • 初始化

    • dp[0] = 1
    • dp[1] = (s[0] >= '1' && s[0] <= '9') ? 1 : 0
      class Solution {
      public:
      int numDecodings(string s) {
         int len = s.size();
         // 动态规划数组 dp:dp[i] 代表前 i 个字符 s[0,...,i-1] 的解码方式数量
         vector<int> dp(len + 1, 0);
         dp[0] = 1;
         dp[1] = (s[0] >= '1' && s[0] <= '9') ? 1 : 0;
         for(int i = 2; i <= len; ++i)
         {
             // 若单个数字 s[i-1] 本身可以解码为一个字母
             if(s[i - 1] >= '1' && s[i - 1] <= '9')
                 dp[i] += dp[i - 1];
             // 若两个数字 s[i-2] s[i-1] 可以解码为一个字母
             if((s[i - 2] == '1' && s[i - 1] >= '0' && s[i - 1] <= '9') ||
                 (s[i - 2] == '2' && s[i - 1] >= '0' && s[i - 1] <= '6'))
                 dp[i] += dp[i - 2];
         }
         return dp[len];
      }
      };
      
      复杂度分析:设字符串长为 N
  • 时间复杂度 O(N):

  • 空间复杂度 O(N):

执行结果:

执行结果:通过

执行用时:4 ms, 在所有 C++ 提交中击败了 14.08% 的用户
内存消耗:6.1 MB, 在所有 C++ 提交中击败了 30.57% 的用户

解法二:状态压缩

上面的动态规划状态 dp[i] 只和 dp[i-2]dp[i-1] 有关,因此可以不用 dp 数组,只设置几个变量即可

class Solution {
public:
    int numDecodings(string s) {
        int len = s.size();
        int dpi2 = 1;                                       // dp[i-2],初始化为 dp[0]
        int dpi1 = (s[0] >= '1' && s[0] <= '9') ? 1 : 0;    // dp[i-1], 初始化为 dp[1]
        int dpi = dpi1;                                     // dp[i]
        for(int i = 1; i < len; ++i)
        {
            dpi = 0;
            if(s[i] >= '1' && s[i] <= '9')
                dpi += dpi1;
            if((s[i - 1] == '1' && s[i] >= '0' && s[i] <= '9') ||
                (s[i - 1] == '2' && s[i] >= '0' && s[i] <= '6'))
                dpi += dpi2;

            dpi2 = dpi1;
            dpi1 = dpi;
        }
        return dpi;
    }
};

复杂度分析:设字符串长为 N

  • 时间复杂度 O(N):
  • 空间复杂度 O(1):

执行结果:

执行结果:通过

执行用时:0 ms, 在所有 C++ 提交中击败了 100.00% 的用户
内存消耗:5.9 MB, 在所有 C++ 提交中击败了 96.18% 的用户