问题描述:
一条包含字母 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 位 的整数。
示例一:
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
示例二:
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6
示例三:
输入:s = "06"
输出:0
解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。
问题解读:
本题主要是将A~B这26个字母等价成1~26个数,然后在给你一段数字字符串,让你通过字母替代
这些数字,最后返回代替的所有的结果的个数。
问题解决:
对应使用的还是动态规划的方法:
1.状态表示
dp[i]等于以i未结尾时,所有的解码方法
2.状态转移方程
假设在i下标之前的两个元素分别是a和b,对dp[i]的结果有两种情况:
1.s[i]单独解码,满足的条件:1 <= a <= 10,对应dp[i] += dp[i - 1]
如果不满足条件,说明a == 0对应无法进行映射,直接返回0
2.s[i] 和s[i - 1]联合解码,满足的条件:10 <= 10*a + b <= 26,对应dp[i] += dp[i - 2]
如果不满足条件,说明无法用英文字母完成映射,直接跳过dp[i] 不加不减。
对应的转台转移方程需要经过判断而存在许多不同,所以这里不列出了。
3.初始化一些值:
根据上述的状态转移方程的描述,知道需要初始化dp[0]和dp[1]。
dp[0]的初始化:
只需要判断dp[0]的值是否满足 1<= dp[0] <= 9,
满足:dp[0] = 1
不满足:说明这一串数字中存在0,直接返回0
dp[1]的初始化:
需要进行两次判断,第一次判断:
需要判断dp[1]的值是否满足 1<= dp[0] <= 9,
满足:dp[1] += dp[0]
不满足:直接返回0
再判断dp[0] 和dp[1]组合是否满足 10 <= 10*dp[0] + dp[1] <= 26,
满足:dp[1] += 1
不满足:dp[1]不加不减
4.填表顺序:
从左向右
5.返回值:
dp[n - 1]
对应的代码如下:
class Solution
{
public:int numDecodings(string s) {int n = s.size();vector<int> dp(n);//初始化前两个位置dp[0] = s[0] != '0';if(n == 1){return dp[0];}if(s[1] <= '9' && s[1] >= '1'){dp[1] += dp[0];}int t = (s[0] - '0') * 10 + s[1] - '0';if(t >= 10 && t <= 26){dp[1] += 1;}//填表for(int i = 2;i < n;i++){//如果单独编码if(s[i] <= '9' && s[i] >= '1'){dp[i] += dp[i - 1];}//如果和前面的一个数联合起来编码int t = (s[i - 1] - '0')* 10 + s[i] - '0';if(t >= 10 && t <= 26){dp[i] += dp[i - 2];}}return dp[n - 1];}
};
大家仔细观察一下这段代码,发现这两段有很多冗余:
那么如何使代码更简洁呢?
我们可以将dp数组多开一个字节,这样就可以将求第二个节点的解法放在循环里面了,从而减少代
码的行数,将第一为作为虚拟头节点那么问题就来了:
1.如何确定虚拟头节点的值是结果是正确的?
我们只需设想现在在求第三个节点的解法有多少个,当第三个节点和前一个节点的组合成立的时候
需要用到虚拟头节点的数字,而此时只要将解法+1即可,所以需要再虚拟头节点存的是数字1.
2.下标的映射如何变化:
应为相当于将dp中的所有的下标都+1了,所以对应到s的映射就需要将下标-1,就是对应要得到的
数字,这不也是添加头节点的关键,不然全盘皆输,所以可以将代码修改如下:
class Solution
{
public:
//优化后int numDecodings(string s) {int n = s.size();vector<int> dp(n + 1);dp[0] = 1;dp[1] = s[1 - 1] != '0';for(int i = 2;i <= n;i++){if(s[i - 1] != '0'){//处理单独编码的情况dp[i] += dp[i - 1];}int t = (s[i - 2] - '0') * 10 + s[i - 1] - '0';if(t >= 10 && t <= 26){dp[i] += dp[i - 2];}}return dp[n];}
};
这样代码就不冗余了,一定要注意s的下标的微妙变化,将所有的s[i] 都变成了s[i - 1],以此类推,
这样的代码虽然代码量下来了,但是么有任何下路上的优化,只是将初始化的值放在循环里进行了
初始化。