一条包含字母 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 位 的整数。
示例 1:
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
示例 3:
输入:s = "0"
输出:0
解释:没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。
示例 4:
输入:s = "06"
输出:0
解释:"06" 不能映射到 "F" ,因为字符串含有前导 0("6" 和 "06" 在映射中并不等价)。
提示:
- 1 <= s.length <= 100
- s 只包含数字,并且可能包含前导零。
解题思路:
由于我们可以将复杂问题分解为更小的子问题,因此采用动态规划算法。
对于字符串s,设 表示字符串 s 的前 i个字符 s[1..i]的解码方法数。在进行状态转移时,我们可以考虑最后一次解码使用了 s 中的哪些字符,那么会有下面的两种情况:
- 第一种是最后一次解码只使用了s中的最后一个字符s[i],只要s[i]不为0,它就可以被解码成A∼I 中的某个字母。由于剩余的前 i−1 个字符的解码方法数为,因此我们可以写出状态转移方程:,其中。
- 第一种是最后一次解码使用了s中的最后两个字符s[i]和s[i-1],只要s[i-1]不为0且s[i]和s[i-1]所组成的整数小于等于26,它就可以被解码成J∼Z 中的某个字母。由于剩余的前 i−2 个字符的解码方法数为,因此我们可以写出状态转移方程:,其中且。
将上面的两种状态转移方程在对应的条件满足时进行累加,即可得到 的值。在动态规划完成后,最终的答案即为 。
注意初始条件:
空字符串可以有 1 种解码方法,解码出一个空字符串。即f0=1。
代码实现:
import javax.swing.undo.UndoableEdit;
import java.sql.SQLOutput;
import java.util.*;
public class Helloworld {
public int numDecodings(String s){
int n = s.length();
int[] f = new int[n+1];
f[0] = 1;
for (int i = 1; i <= n; i++) {
if(s.charAt(i-1) != '0')
f[i] += f[i-1];
if(i > 1 && s.charAt(i-2) != '0' && ((s.charAt(i-2)-'0')*10+s.charAt(i-1)-'0') <= 26)
f[i] += f[i-2];
}
return f[n];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String string = scanner.nextLine();
System.out.println(new Helloworld().numDecodings(string));
}
}