A message containing letters from A-Z is being encoded to numbers using the following mapping:

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

Given a non-empty string containing only digits, determine the total number of ways to decode it.

Example 1:

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

这道题要求解码方法,跟之前那道 Climbing Stairs 非常的相似,但是还有一些其他的限制条件,比如说一位数时不能为0,两位数不能大于 26,其十位上的数也不能为0,除去这些限制条件,跟爬梯子基本没啥区别,也勉强算特殊的斐波那契数列,当然需要用动态规划 Dynamci Programming 来解。建立一维 dp 数组,其中 dp[i] 表示s中前i个字符组成的子串的解码方法的个数,长度比输入数组长多多1,并将 dp[0] 初始化为1。现在来找状态转移方程,dp[i] 的值跟之前的状态有着千丝万缕的联系,就拿题目中的例子2来分析吧,当 i=1 时,对应s中的字符是 s[0]='2',只有一种拆分方法,就是2,注意 s[0] 一定不能为0,这样的话无法拆分。当 i=2 时,对应s中的字符是 s[1]='2',由于 s[1] 不为0,那么其可以被单独拆分出来,就可以在之前 dp[i-1] 的每种情况下都加上一个单独的2,这样 dp[i] 至少可以有跟 dp[i-1] 一样多的拆分情况,接下来还要看其能否跟前一个数字拼起来,若拼起来的两位数小于等于26,并且大于等于 10(因为两位数的高位不能是0),那么就可以在之前 dp[i-2] 的每种情况下都加上这个二位数,所以最终 dp[i] = dp[i-1] + dp[i-2],是不是发现跟斐波那契数列的性质吻合了。所以0是个很特殊的存在,若当前位置是0,则一定无法单独拆分出来,即不能加上 dp[i-1],就只能看否跟前一个数字组成大于等于 10 且小于等于 26 的数,能的话可以加上 dp[i-2],否则就只能保持为0了。具体的操作步骤是,在遍历的过程中,对每个数字首先判断其是否为0,若是则将 dp[i] 赋为0,若不是,赋上 dp[i-1] 的值,然后看数组前一位是否存在,如果存在且满足前一位是1,或者和当前位一起组成的两位数不大于 26,则当前 dp[i] 值加上 dp[i - 2]。最终返回 dp 数组的最后一个值即可,代码如下:

C++ 解法一:

class Solution {
public:
int numDecodings(string s) {
if (s.empty() || s[] == '') return ;
vector<int> dp(s.size() + , );
dp[] = ;
for (int i = ; i < dp.size(); ++i) {
dp[i] = (s[i - ] == '') ? : dp[i - ];
if (i > && (s[i - ] == '' || (s[i - ] == '' && s[i - ] <= ''))) {
dp[i] += dp[i - ];
}
}
return dp.back();
}
};

Java 解法一:

class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt() == '') return ;
int[] dp = new int[s.length() + ];
dp[] = ;
for (int i = ; i < dp.length; ++i) {
dp[i] = (s.charAt(i - ) == '') ? : dp[i - ];
if (i > && (s.charAt(i - ) == '' || (s.charAt(i - ) == '' && s.charAt(i - ) <= ''))) {
dp[i] += dp[i - ];
}
}
return dp[s.length()];
}
}

下面这种方法跟上面的方法的思路一样,只是写法略有不同:

C++ 解法二:

class Solution {
public:
int numDecodings(string s) {
if (s.empty() || s[] == '') return ;
vector<int> dp(s.size() + , );
dp[] = ;
for (int i = ; i < dp.size(); ++i) {
if (s[i - ] != '') dp[i] += dp[i - ];
if (i >= && s.substr(i - , ) <= "" && s.substr(i - , ) >= "") {
dp[i] += dp[i - ];
}
}
return dp.back();
}
};

Java  解法二:

class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int[] dp = new int[s.length() + 1];
dp[0] = 1;
for (int i = 1; i < dp.length; ++i) {
if (s.charAt(i - 1) != '0') dp[i] += dp[i - 1];
if (i >= 2 && (s.substring(i - 2, i).compareTo("10") >= 0 && s.substring(i - 2, i).compareTo("26") <= 0)) {
dp[i] += dp[i - 2];
}
}
return dp[s.length()];
}
}

我们再来看一种空间复杂度为 O(1) 的解法,用两个变量 a, b 来分别表示 s[i-1] 和 s[i-2] 的解码方法,然后从 i=1 开始遍历,也就是字符串的第二个字符,判断如果当前字符为 '0',说明当前字符不能单独拆分出来,只能和前一个字符一起,先将 a 赋为0,然后看前面的字符,如果前面的字符是1或者2时,就可以更新 a = a + b,然后 b = a - b,其实 b 赋值为之前的 a,如果不满足这些条件的话,那么 b = a,参见代码如下:

C++ 解法三:

class Solution {
public:
int numDecodings(string s) {
if (s.empty() || s[] == '') return ;
int a = , b = , n = s.size();
for (int i = ; i < n; ++i) {
if (s[i] == '') a = ;
if (s[i - ] == '' || (s[i - ] == '' && s[i] <= '')) {
a = a + b;
b = a - b;
} else {
b = a;
}
}
return a;
}
};

Java 解法三:

class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int a = 1, b = 1, n = s.length();
for (int i = 1; i < n; ++i) {
if (s.charAt(i) == '0') a = 0;
if (s.charAt(i - 1) == '1' || (s.charAt(i - 1) == '2' && s.charAt(i) <= '6')) {
a = a + b;
b = a - b;
} else {
b = a;
}
}
return a;
}
}

Github 同步地址:

https://github.com/grandyang/leetcode/issues/91

类似题目:

Decode Ways II

Climbing Stairs

参考资料:

https://leetcode.com/problems/decode-ways/

https://leetcode.com/problems/decode-ways/discuss/30384/a-concise-dp-solution

https://leetcode.com/problems/decode-ways/discuss/30462/accepted-solution-to-decode-ways-no-need-to-take-care-of-0-case

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Decode Ways 解码方法的更多相关文章

  1. [LeetCode] Decode Ways 解码方法个数、动态规划

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  2. Leetcode 91. Decode Ways 解码方法(动态规划,字符串处理)

    Leetcode 91. Decode Ways 解码方法(动态规划,字符串处理) 题目描述 一条报文包含字母A-Z,使用下面的字母-数字映射进行解码 'A' -> 1 'B' -> 2 ...

  3. [LeetCode] 91. Decode Ways 解码方法

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  4. [LintCode] Decode Ways 解码方法

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  5. 091 Decode Ways 解码方法

    包含 A-Z 的字母的消息通过以下规则编码:'A' -> 1'B' -> 2...'Z' -> 26给定一个包含数字的编码消息,请确定解码方法的总数.例如,给定消息为 "1 ...

  6. Leetcode91.Decode Ways解码方法

    一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只包含数字的非空字符串,请计算解码方法的总数. 示例 1 ...

  7. [leetcode]91. Decode Ways解码方法

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  8. [LeetCode] decode ways 解码方式

    A message containing letters fromA-Zis being encoded to numbers using the following mapping: 'A' -&g ...

  9. [LeetCode] Decode Ways II 解码方法之二

    A message containing letters from A-Z is being encoded to numbers using the following mapping way: ' ...

随机推荐

  1. Android立体旋转动画实现与封装(支持以X、Y、Z三个轴为轴心旋转)

    本文主要介绍Android立体旋转动画,或者3D旋转,下图是我自己实现的一个界面 立体旋转分为以下三种: 1. 以X轴为轴心旋转 2. 以Y轴为轴心旋转 3. 以Z轴为轴心旋转--这种等价于andro ...

  2. Java进击C#——语法之线程同步

    上一章我们讲到关于C#线程方向的应用.但是笔者并没有讲到多线程中的另一个知识点--同步.多线程的应用开发都有可能发生脏数据.同步的功能或多或少都会用到.本章就要来讲一下关于线程同步的问题.根据笔者这几 ...

  3. Moon.Orm 入门总指南

    注意:下面的pdf文件强烈建议下载或在线查看 1)旗舰版帮助文档点击查看或下载 2)http://pan.baidu.com/s/1hq7krFu(新手手册下载)(强烈推荐) 3)性能及规范下载,网友 ...

  4. scikit-learn预处理实例之一:使用FunctionTransformer选择列

    本例展示怎样在一个管道中使用FunctionTransformer.如果你知道你的数据集的第一主成分与分类任务无关,你可以使用FunctionTransformer选取除PCA转化的数据的第一列之外的 ...

  5. 【QuickHit项目实例】

    关于java面向对象QuickHit项目实例 Game类:用来得到随机出现的字符串(随机打印的字符串,然后玩家进行输入字符串) public class Game { private String s ...

  6. JDBC_part2_DML以及预编译_编写DBUtil工具类

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! jdbc day02 DML语法 比起插叙语句,没有R ...

  7. 五个典型的 JavaScript 面试题

    阅读原文 在IT界,需要大量的 JavaScript 开发者.如果你的能力能够胜任这一角色,那么你将获得许多更换工作和提高薪水的机会.但是在你被公司录取之前,你需要展现你的技术实力,以便通过面试环节. ...

  8. JqueryDataTable的使用(.Net平台)

    上一篇随笔提到了MvcPager,最近用到了一款前端JQ插件------DataTable(简称DT),很好用. DT是一款前端插件,和后端完全分离开,就这点来看,我就特别喜欢. 一.使用DT,需要以 ...

  9. 基于Nuclear的Web组件-Todo的十一种写法

    刀耕火种 刀耕火种是新石器时代残留的农业经营方式.又称迁移农业,为原始生荒耕作制. var TodoApp = Nuclear.create({ add: function (evt) { evt.p ...

  10. [deviceone开发]-底部弹出选择

    一.简介 个人上传的第一个示例源码,两天空闲时间写的,一点简单组件,写的挺乱还没啥注释,仅供新手学习. 底部弹出选择,可滑动选择选项,如果停留在选项中间,可自动校正位置,加了一点简单的动画效果,需要的 ...