[LeetCode] Decode Ways II 解码方法之二
A message containing letters from A-Z is being encoded to numbers using the following mapping way:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Beyond that, now the encoded string can also contain the character '*', which can be treated as one of the numbers from 1 to 9.
Given the encoded message containing digits and the character '*', return the total number of ways to decode it.
Also, since the answer may be very large, you should return the output mod 109 + 7.
Example 1:
Input: "*"
Output: 9
Explanation: The encoded message can be decoded to the string: "A", "B", "C", "D", "E", "F", "G", "H", "I".
Example 2:
Input: "1*"
Output: 9 + 9 = 18
Note:
- The length of the input string will fit in range [1, 105].
- The input string will only contain the character '*' and digits '0' - '9'.
这道解码的题是之前那道Decode Ways的拓展,难度提高了不少,引入了星号,可以代表1到9之间的任意数字,是不是有点外卡匹配的感觉。有了星号以后,整个题就变得异常的复杂,所以结果才让我们对一个很大的数求余,避免溢出。这道题的难点就是要分情况种类太多,一定要全部理通顺才行。我们还是用DP来做,建立一个一维dp数组,其中dp[i]表示前i个字符的解码方法等个数,长度为字符串的长度加1。将dp[0]初始化为1,然后我们判断,如果字符串第一个字符是0,那么直接返回0,如果是*,则dp[1]初始化为9,否则初始化为1。下面就来计算一般情况下的dp[i]了,我们从i=2开始遍历,由于要分的情况种类太多,我们先选一个大分支,就是当前遍历到的字符s[i-1],只有三种情况,要么是0,要么是1到9的数字,要么是星号。我们一个一个来分析:
首先来看s[i-1]为0的情况,这种情况相对来说比较简单,因为0不能单独拆开,只能跟前面的数字一起,而且前面的数字只能是1或2,其他的直接返回0即可。那么当前面的数字是1或2的时候,dp[i]的种类数就跟dp[i-2]相等,可以参见之前那道Decode Ways的讲解,因为后两数无法单独拆分开,就无法产生新的解码方法,所以只保持住原来的拆分数量就不错了;如果前面的数是星号的时候,那么前面的数可以为1或者2,这样就相等于两倍的dp[i-2];如果前面的数也为0,直接返回0即可。
再来看s[i-1]为1到9之间的数字的情况,首先搞清楚当前数字是可以单独拆分出来的,那么dp[i]至少是等于dp[i-1]的,不会拖后腿,还要看其能不能和前面的数字组成两位数进一步增加解码方法。那么就要分情况讨论前面一个数字的种类,如果当前数字可以跟前面的数字组成一个小于等于26的两位数的话,dp[i]还需要加上dp[i-2];如果前面的数字为星号的话,那么要看当前的数字是否小于等于6,如果是小于等于6,那么前面的数字就可以是1或者2了,此时dp[i]需要加上两倍的dp[i-2],如果大于6,那么前面的数字只能是1,所以dp[i]只能加上dp[i-2]。
最后来看s[i-1]为星号的情况,如果当前数字为星号,那么就创造9种可以单独拆分的方法,所以那么dp[i]至少是等于9倍的dp[i-1],还要看其能不能和前面的数字组成两位数进一步增加解码方法。那么就要分情况讨论前面一个数字的种类,如果前面的数字是1,那么当前的9种情况都可以跟前面的数字组成两位数,所以dp[i]需要加上9倍的dp[i-2];如果前面的数字是2,那么只有小于等于6的6种情况都可以跟前面的数字组成两位数,所以dp[i]需要加上6倍的dp[i-2];如果前面的数字是星号,那么就是上面两种情况的总和,dp[i]需要加上15倍的dp[i-2]。
每次算完dp[i]别忘了对超大数取余,参见代码如下:
解法一:
class Solution {
public:
int numDecodings(string s) {
int n = s.size(), M = 1e9 + ;
vector<long> dp(n + , );
dp[] = ;
if (s[] == '') return ;
dp[] = (s[] == '*') ? : ;
for (int i = ; i <= n; ++i) {
if (s[i - ] == '') {
if (s[i - ] == '' || s[i - ] == '') {
dp[i] += dp[i - ];
} else if (s[i - ] == '*') {
dp[i] += * dp[i - ];
} else {
return ;
}
} else if (s[i - ] >= '' && s[i - ] <= '') {
dp[i] += dp[i - ];
if (s[i - ] == '' || (s[i - ] == '' && s[i - ] <= '')) {
dp[i] += dp[i - ];
} else if (s[i - ] == '*') {
dp[i] += (s[i - ] <= '') ? ( * dp[i - ]) : dp[i - ];
}
} else { // s[i - 1] == '*'
dp[i] += * dp[i - ];
if (s[i - ] == '') dp[i] += * dp[i - ];
else if (s[i - ] == '') dp[i] += * dp[i - ];
else if (s[i - ] == '*') dp[i] += * dp[i - ];
}
dp[i] %= M;
}
return dp[n];
}
};
下面这种解法是论坛上排名最高的解法,常数级的空间复杂度,写法非常简洁,思路也巨牛逼,博主是无论如何也想不出来的,只能继续当搬运工了。这里定义了一系列的变量e0, e1, e2, f0, f1, f2。其中:
e0表示当前可以获得的解码的次数,当前数字可以为任意数 (也就是上面解法中的dp[i])
e1表示当前可以获得的解码的次数,当前数字为1
e2表示当前可以获得的解码的次数,当前数字为2
f0, f1, f2分别为处理完当前字符c的e0, e1, e2的值
那么下面我们来进行分类讨论,当c为星号的时候,f0的值就是9*e0 + 9*e1 + 6*e2,这个应该不难理解了,可以参考上面解法中的讲解,这里的e0就相当于dp[i-1],e1和e2相当于两种不同情况的dp[i-2],此时f1和f2都赋值为e0,因为要和后面的数字组成两位数的话,不会增加新的解码方法,所以解码总数跟之前的一样,为e0, 即dp[i-1]。
当c不为星号的时候,如果c不为0,则f0首先应该加上e0。然后不管c为何值,e1都需要加上,总能和前面的1组成两位数;如果c小于等于6,可以和前面的2组成两位数,可以加上e2。然后我们更新f1和f2,如果c为1,则f1为e0;如果c为2,则f2为e0。
最后别忘了将f0,f1,f2赋值给e0,e1,e2,其中f0需要对超大数取余,参见代码如下:
解法二:
class Solution {
public:
int numDecodings(string s) {
long e0 = , e1 = , e2 = , f0, f1, f2, M = 1e9 + ;
for (char c : s) {
if (c == '*') {
f0 = * e0 + * e1 + * e2;
f1 = e0;
f2 = e0;
} else {
f0 = (c > '') * e0 + e1 + (c <= '') * e2;
f1 = (c == '') * e0;
f2 = (c == '') * e0;
}
e0 = f0 % M;
e1 = f1;
e2 = f2;
}
return e0;
}
};
下面这解法由热心网友edyyy提供,在解法二的基础上去掉了两个变量,节省了行数,很符合博主的极简风格,参见代码如下:
解法三:
class Solution {
public:
int numDecodings(string s) {
long e0 = , e1 = , e2 = , f0 = , M = 1e9 + ;
for (char c : s) {
if (c == '*') {
f0 = * e0 + * e1 + * e2;
e1 = e0;
e2 = e0;
} else {
f0 = (c > '') * e0 + e1 + (c <= '') * e2;
e1 = (c == '') * e0;
e2 = (c == '') * e0;
}
e0 = f0 % M;
}
return e0;
}
};
类似题目:
参考资料:
https://discuss.leetcode.com/topic/95301/python-straightforward-with-explanation
https://discuss.leetcode.com/topic/95518/java-o-n-by-general-solution-for-all-dp-problems
https://discuss.leetcode.com/topic/95204/java-dp-solution-o-n-time-and-space-some-explanations
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Decode Ways II 解码方法之二的更多相关文章
- [LeetCode] 639. Decode Ways II 解码方法 II
A message containing letters from A-Z is being encoded to numbers using the following mapping way: ' ...
- LeetCode OJ:Decode Ways(解码方法)
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...
- [LeetCode] Decode Ways 解码方法
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...
- 【JavaScript】【dp】Leetcode每日一题-解码方法
[JavaScript]Leetcode每日一题-解码方法 [题目描述] 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 'A' -> 1 'B' -> 2 ... 'Z' -& ...
- [Swift]LeetCode639. 解码方法 2 | Decode Ways II
A message containing letters from A-Z is being encoded to numbers using the following mapping way: ' ...
- [LeetCode] Decode Ways 解码方法个数、动态规划
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...
- leetcode 639 Decode Ways II
首先回顾一下decode ways I 的做法:链接 分情况讨论 if s[i]=='*' 考虑s[i]单独decode,由于s[i]肯定不会为0,因此我们可以放心的dp+=dp1 再考虑s[i-1] ...
- [LeetCode] Decode Ways [33]
题目 A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A ...
- LeetCode:Decode Ways 解题报告
Decode WaysA message containing letters from A-Z is being encoded to numbers using the following map ...
随机推荐
- QueryBuilder 前端构造SQL条件的插件使用方法
页面引入JS等: <script type="text/javascript" src="/qysds-jx/pages/gzrw/js/jquery.js&quo ...
- Git中一些远程库操作的细节
最近在公司,老是遇到Git远程操作的问题,现总结如下: 1,本地checkout一个新的分支,向远程push的时候,若远程没有该分支,会新建一个. 2.将远程代码clone到本地修改并commit后, ...
- 第二次作业之微信小程序
2.1 介绍产品相关信息 你选择的产品是? 微信小程序 为什么选择该产品作为分析? 在等待了1年多以后,小程序终于在今年初上线,即速应用在H5领域的累计,便承接在小程序上.8月7日,即速应用的用户微信 ...
- 项目Alpha冲刺Day12
一.会议照片 二.项目进展 1.今日安排 修复全局的日期转换问题,完成用户所有相关的模块,对全局的异常处理做优化.其他模块进行一部分实现. 2.问题困难 全局异常处理后发现没有进行按照链进行下去,造成 ...
- Ubuntu下安装gsoap
昨天在ubuntu下进行安装gSOAP,费了很多时间,没成功,今天又来找了大量教程资料,终于一次成功,这里写下自己的安装步骤和方法,供大家参考. 首先下载gsoap,我下载的是gsoap-2.8.1. ...
- Mongodb 3 查询优化(语句优化、建索引)
一.explain(),语句分析工具 MongoDB 3.0之后,explain的返回与使用方法与之前版本有了很大的变化,介于3.0之后的优秀特色和我们目前所使用给的是3.0.7版本,本文仅针对Mon ...
- c# windows service 实现监控其他程序是否被关闭,关闭则报警
namespace MonitorService { public partial class MonitorSv : ServiceBase { string AppName = "&qu ...
- VMware网络配置
NAT模式 首先保证虚拟机网卡和主机对接,虚拟机网络连接要和主机在同一网段 1. 控制面板\网络和 Internet\网络连接中配置VMnet8 2. 编辑虚拟机网络配置 此处子网ip需要和Vnet8 ...
- js回顾(DOM中标签的CRUD,表格等)
01-DOM中的创建和添加标签 02-删除替换克隆标签 03-全选全不选反选 04-新闻字体 05-表格增删 06-动态生成表格 07-表格隔行变色 08-左到右右到左(将左边的标签移动到右边) 09 ...
- Linux入门:usermod - 修改用户帐户信息
一.什么是usermod? usermod 命令通过修改系统帐户文件来修改用户账户信息usermod [options] user_name选项(options)-a|--append ##把用户追加 ...