(1)题目描述
一条包含字母 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 位 的整数。
(二)解题思路
引用力扣中一位老哥的话:确定状态转移方程5分钟,处理边界条件2小时。还是动态规划三部曲:
(1)确定dp[i]含义:ch[i-1]位置解码方法总数
(2)初始值。此题有一个很奇妙的设置。设置dp[i+1]为ch[i]位置的解码总数,就说明dp[0]位置无实际意义,为什么要这样设置呢?因为该题的状态转移方程涉及dp[i]=dp[i-1]+dp[i-2];若此时i为1,则此时就是dp[2],那么就不会出现i-2时下标越界的情况。dp[0]=dp[1]=1;
(3)状态转移方程
1)ch[i]=0时:若ch[i-1]='1'或者ch[i-1]='2',说明0可以与前面的数字合并译码成一个数,那么此时dp[i+1]=dp[i-1](因为dp的下标比原数组ch对应的下标大1)
2)ch[i]!=0时:若ch[i-1]='1',则无论ch[i]为何值都可进行翻译,此时dp[i+1]=dp[i]+dp[i-1](可以自己下去推一推,找规律),若ch[i-1]=='2'&&ch[i]>='0'&&ch[i]<='6',此时同上;若以上两种都不满足,此时只能ch[i]进行单独的翻译,不能同前者联合翻译,所以此时dp[i+1]=dp[i];
(三)代码如下:
class Solution17 {
public int numDecodings(String s) {
char[] ch=s.toCharArray();
int[] dp=new int[ch.length];
// (1)定义dp[i]的含义:dp[i]为char[i]的译码总数
// (2)初始值!边界条件很多
//很妙!dp[0]无实际含义,dp[1]表示ch[0]的译码总数
if(ch[0]=='0'){
return 0;
}
dp[0]=1;
dp[1]=1;
for (int i = 1; i < ch.length; i++) {
if(ch[i]=='0'){
if(ch[i-1]=='1'||ch[i-1]=='2'){
dp[i+1]=dp[i-1];
}else{
return 0;
}
}else{ //当s不为0时
if(ch[i-1]=='1'||(ch[i-1]=='2'&&ch[i]>='0'&&ch[i]<='6')){
dp[i+1]=dp[i]+dp[i-1];
}else{
dp[i+1]=dp[i];
}
}
}
return dp[ch.length-1];
}
}