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:

  1. The length of the input string will fit in range [1, 105].
  2. 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;
}
};

类似题目:

Decode Ways

参考资料:

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 解码方法之二的更多相关文章

  1. [LeetCode] 639. Decode Ways II 解码方法 II

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

  2. LeetCode OJ:Decode Ways(解码方法)

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

  3. [LeetCode] Decode Ways 解码方法

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

  4. 【JavaScript】【dp】Leetcode每日一题-解码方法

    [JavaScript]Leetcode每日一题-解码方法 [题目描述] 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 'A' -> 1 'B' -> 2 ... 'Z' -& ...

  5. [Swift]LeetCode639. 解码方法 2 | Decode Ways II

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

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

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

  7. leetcode 639 Decode Ways II

    首先回顾一下decode ways I 的做法:链接 分情况讨论 if s[i]=='*' 考虑s[i]单独decode,由于s[i]肯定不会为0,因此我们可以放心的dp+=dp1 再考虑s[i-1] ...

  8. [LeetCode] Decode Ways [33]

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

  9. LeetCode:Decode Ways 解题报告

    Decode WaysA message containing letters from A-Z is being encoded to numbers using the following map ...

随机推荐

  1. windows2003NLB群集配置(工作组和域)

    详情地址查看:http://wenku.baidu.com/link?url=kiyyeZcSXZV9vLIZFbAlQXq1Qsm3_N0A7cue-qz6CorUYbXaGn-Ocfo49Qyal ...

  2. vue小白快速入门

    一.vue是什么 Vue 是一套用于构建用户界面的渐进式框架. 压缩后仅有17kb 二.vue环境搭建 你直接下载并用 <script> 标签引入,Vue 会被注册为一个全局变量. 但在用 ...

  3. JavaScript(第九天)【正则表达式】

    假设用户需要在HTML表单中填写姓名.地址.出生日期等.那么在将表单提交到服务器进一步处理前,JavaScript程序会检查表单以确认用户确实输入了信息并且这些信息是符合要求的.   一.什么是正则表 ...

  4. Rails + React +antd + Redux环境搭建

    前提条件:node和ruby on rails必须已经安装好(相关安装流程不再此处介绍) 1.nvm.node 2.npm or yarn装一个就好 3.rvm.ruby on rails 4.for ...

  5. 随机四则运算的出题程序java

    一.设计思想 1.功能较多必须有菜单选择项,将一个大程序分为若干个功能模块的小程序,逐个实现2.针对题目避免重复时先将已生成的算式保存,然后将下一条生成的式子进行判断是否已生成,如果生成则返回循环语句 ...

  6. Django 基本设置

    建立django目录,为了独立区分app和主站的关系,需要把app完全和主站分离 app/views.py from django.shortcuts import render from djang ...

  7. C简单实现动态顺序表

    <span style="font-size:18px;">一下为简单实现:</span> #define SIZE 3; typedef int Data ...

  8. hi-nginx-1.4.2发布,多项重要更新

    支持多种编程语言混合开发web应用的通用服务器hi-nginx-1.4.2已经发布了. 此次发布包含多项重要更新: 支持python2和3,通过编译选项--with-http-hi-python-ve ...

  9. OpenShift实战(一):OpenShift高级安装

    1.1 服务器基本信息 本次安装采用一个master.5个node.3个etcd,node节点两块硬盘,60G磁盘用于docker storage,xxx改为自己的域名或主机名. 节点 功能 IP 内 ...

  10. 【转】Python处理wave文件

    #本文PDF版下载 Python解析Wav文件并绘制波形的方法 #本文代码下载 Wav波形绘图代码 #本文实例音频文件night.wav下载 音频文件下载 (石进-夜的钢琴曲) 前言 在现在繁忙的生活 ...