> 技术文档 > 【动态规划:斐波那契数列模型】解码方法

【动态规划:斐波那契数列模型】解码方法

在这里插入图片描述

91. 解码方法

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

\'A\' -> \"1\"\'B\' -> \"2\"...\'Z\' -> \"26\"

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

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

注意,消息不能分组为 (1 11 06) ,因为 \"06\" 不能映射为 \"F\" ,这是由于 \"6\"\"06\" 在映射中并不等价。

给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数

题目数据保证答案肯定是一个 32 位 的整数。

示例 1:

输入:s = \"12\"输出:2解释:它可以解码为 \"AB\"(1 2)或者 \"L\"(12)。

示例 2:

输入:s = \"226\"输出:3解释:它可以解码为 \"BZ\" (2 26), \"VF\" (22 6), 或者 \"BBF\" (2 2 6) 。

示例 3:

输入:s = \"06\"输出:0解释:\"06\" 无法映射到 \"F\" ,因为存在前导零(\"6\" 和 \"06\" 并不等价)。

提示:

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

解题思路

​ 下图中给出了动态规划表的状态表示,还有状态转移方程:

在这里插入图片描述

​ 值得注意的是,要具体编码的时候,s[i]s[i-1] 一起解码这种情况10 <= a*10 + b <= 26,其中 ab 是字符,所以 必须让它们都减去 ‘0’ 才能得到对应的数字大小,这个要注意,它们并不是个位数字本身!

​ 还有就是为什么不讨论 s[i+1]s[i] 解码的情况呢❓❓❓这是因为我们 dp[i] 表示的是从头到 i 位置处的解码方法总数,所以当然不用考虑 i 后面的字符情况!

​ 现在来讨论一下初始化的问题,因为我们是从左向右推导,并且涉及到 dp[i-2]dp[i-1],那么势必要将前两个元素进行初始化。

​ 下面还是用图片的形式来给出初始化的问题:

在这里插入图片描述

class Solution {public: int numDecodings(string s) { // 创建dp表,dp[i]:以i结尾时的解法总数 int n = s.size(); vector<int> dp(n); // 初始化dp[0]和dp[1],因为状态转移方程需要借助到i-1和i-2 if(s[0] != \'0\') dp[0] = 1; if(n == 1) return dp[0]; // 处理一下边界问题,因为题目范围从1个字符开始 if(s[1] != \'0\' && s[0] != \'0\') // 单独编码的情况 dp[1]++; int tmp = s[1]-\'0\' + (s[0]-\'0\')*10; // 合起来编码的情况 if(tmp >= 10 && tmp <= 26) dp[1]++; // 填表 for(int i = 2; i < n; ++i) { if(s[i] != \'0\') // 单独编码的情况 dp[i] += dp[i - 1]; // 合起来编码的情况 tmp = s[i]-\'0\' + (s[i - 1]-\'0\')*10; if(tmp >= 10 && tmp <= 26) dp[i] += dp[i - 2]; } return dp[n - 1]; }};

优化

​ 与其说是优化,不如说是处理边界问题以及初始化问题的技巧!

​ 从上面这段代码可以发现,其实这个 dp[1] 和我们在 for 循环中判断是一模一样的,只不过 dp[1] 变成了 dp[i],仅此而已。那么这样子冗余的代码其实是不太好看的,写起来也是非常别扭,所以我们来将其优化一下!

​ 其实思路很简单,既然 dp[1] 的判断和后面是一样的,而只有 dp[0] 是需要单独拎出来做额外判断的,那么我们就 先多给 dp 表一个空间,然后将整体往后移动一位,并且将新的 dp[0] 作为虚拟位来填充,如下图所示:

在这里插入图片描述

class Solution {public: int numDecodings(string s) { // 优化 int n = s.size(); vector<int> dp(n + 1); // 多开一个空间 dp[0] = 1; // 第一位是虚拟位,为了保证第三个字符填表时的正确,这里填1,注意不能为0 if(s[0] != \'0\') // dp[1]相当于原来的dp[0] dp[1] = 1; // 填表,注意是遍历到n,并且原来的关于s的下标都要再减一,因为dp表全体往后移动了一位 for(int i = 2; i <= n; ++i) { if(s[i - 1] != \'0\') // 单独编码的情况 dp[i] += dp[i - 1]; // 合起来编码的情况 int tmp = s[i - 1]-\'0\' + (s[i - 2]-\'0\')*10; if(tmp >= 10 && tmp <= 26) dp[i] += dp[i - 2]; } return dp[n]; }};

在这里插入图片描述