本文共 1952 字,大约阅读时间需要 6 分钟。
A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1'B' -> 2...'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,
Given encoded message "12"
, it could be decoded as "AB"
(1 2) or "L"
(12).
The number of ways decoding "12"
is 2.
这是个DP问题,最关键的就是找到状态转移方程,但这个状态转移方程还真难住了,想到一个果断敲进去,发现错了,就一筹莫展。百度了下,结果也全是直接给的大同小异的代码,转移方程的产生过程没有一个提起。这也难怪,都是大牛,我等菜鸟望尘莫及。想不出来我们可以写个最规范的例子来画一画,至于特殊的情况我们不去考虑先。
尽管我们不知道状态转移方程的确切形式,但是我们总应该想到用f[ i ] 来表示前 i 个字符的解码方法数。这就足够了。
这里得到的状态转移方程虽然是理想情况下的,但是毕竟我们还是发现了其中的规律,s[ i ]跟 s[ i - 1]构成的关系分为以下几种:
1)s[ i ] 可以独立存在,即可以被单独解码,而且s[ i ] 跟前面的字符组合后形成的组合数也可以被解码,即也是合法的编码。
2)s[ i ] 可以独立存在,即可以被单独解码,但是不能跟前面的字符组合形成组合数,即组合数的编码非法,不能被解析。
3)s[ i ] 不能独立存在,这只有一种情况,就是s[ i ] 为 ‘0’的时候,但可以依附前面的字符,形成组合数,构成合法编码。
4)s[ i ] 不能独立存在,而且也不能跟前面的字符形成组合数,这时,就是错误的编码,无法解析。
那么我们看下,在这些情况下我们的状态方程是什么样的。
1)这当然是我们在上图表中发现的完美规律了,f [ i ] = f [ i - 1] + f [ i - 2]
2)这种情况就是我们在图表中总结的第1点,所以这时 f[ i ] = f [ i - 1]
3)这种情况就是我们在图表中总结的第 2 点,这时 f [ i ] = f [ i - 2]
好了,问题分析到这里,我想大家都跃跃欲试了,差不多,可以开始了,至于怎么判断合法不合法,每个人的判断方法不同,实现也就不太一样,但这无关紧要,最关键的是我们分析清楚状态转移方程。
注意:
1.这里我们在设定状态的时候,因为 关系到 f[ i - 2] ,所以我们还是多开几个空间为好。
2.判断下s[ 0 ],若为‘0’ 就返回吧。
//CODE
class Solution {public: int numDecodings(string s) { vector dp(s.size() + 3, 1); if(s.size() == 0 || s[0] == '0') return 0; for(int i = 1; i < s.size(); ++i) { // i start from 0, j start from 1. int j = i + 1; //(1):s[i]这个0既不能独立存在,也跟前面的结合不了,不能解析。 if(s[i] == '0' && (s[i - 1] == '0' || s[i - 1] > '2')) return 0; //(2):s[i]这个0虽然不能独立存在,但是可以傍上s[i-1]这个大款,形成组合数。 else if(s[i] == '0') dp[j] = dp[j - 2]; //(3):s[i]可以独立存在,但是无法与前面的s[i-1]结合,构成组合数。 else if(s[i-1] == '0'|| s[i - 1] > '2' || s[i - 1] == '2' && s[i] > '6') dp[j] = dp[j - 1]; //(4):s[i]可以独立存在,也可以与前面的s[i - 1]组成结合数。 else dp[j] = dp[j - 1] + dp[j - 2]; //独立存在:可以被解析。 //构成组合数:组合数可以被解析。 } return dp[s.size()]; }};//, 转载请说明出处。