题目链接

LeetCode

题目描述

一条包含字母 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 只包含数字,并且可能包含前导零。

    方法一:递归 回溯 超时,不推荐

    1. class Solution {
    2. private int res = 0;
    3. public int numDecodings(String s) {
    4. if(s.charAt(0) == '0'){
    5. return 0;
    6. }
    7. backstrack(0, s, s.length());
    8. return res;
    9. }
    10. private void backstrack(int start, String s, int len){
    11. if(start == len || start + 1 == len ){
    12. ++res;
    13. return;
    14. }
    15. if(s.charAt(start) == '0'){
    16. return;
    17. }
    18. if(s.charAt(start) == '1'){
    19. backstrack(start+1, s, len);
    20. backstrack(start+2, s, len);
    21. }else if(s.charAt(start) == '2' && s.charAt(start+1) < '7'){
    22. backstrack(start+1, s, len);
    23. backstrack(start+2, s, len);
    24. }else{
    25. backstrack(start+1, s, len);
    26. }
    27. }
    28. }

方法二:动态规划

dp[i] 代表下标为 i 时的解码个数

  1. 当字符串第一个字符为 ‘0’ 时,说明不能解码, 返回 0
  2. 第一个字符至少可以解码为一个编码 所以 dp[1] = 1, 空字符串可以有 1 种解码方法,解码出一个空字符串,所以dp[0] = 1。
  3. 从下标为1,也就是第二个字符开始遍历
    1. 如果当前字符为 ‘0’,只能是和前一个字符组成 ‘10’ 或者 ‘20’ 一种可能,所以结果和当前位置前2位的结果相同。 否则,解码错误 返回 0。
    2. 如果当前下标的字符和上一个下标的字符组成 11—26 的编码,则有两种可能
      1. 分为两个字母或者一个字母 所以结果是 dp[i + 1] = dp[i] + dp[i - 1], dp[i]是解析为两个字母的个数, dp[i-1] 是解析为两个字母的个数
    3. 如果当前下标的字符和上一个下标的字符无法组成合法的字母 则 只能表示一个字母,结果为dp[i + 1] = dp[i];
    4. 返回最后的结果
  1. class Solution {
  2. private int res = 0;
  3. public int numDecodings(String s) {
  4. if(s.charAt(0) == '0'){
  5. return 0;
  6. }
  7. int[] dp = new int[s.length() + 1];
  8. // 空也是一种
  9. dp[0] = dp[1] = 1;
  10. for(int i = 1; i < s.length(); ++i){
  11. if(s.charAt(i) == '0'){
  12. if(s.charAt(i-1) == '1' || s.charAt(i-1) == '2'){
  13. dp[i + 1] = dp[i - 1];
  14. }else{
  15. return 0;
  16. }
  17. }else{
  18. if(s.charAt(i-1) == '1' || (s.charAt(i-1) == '2' && s.charAt(i) <= '6')){
  19. dp[i + 1] = dp[i] + dp[i - 1];
  20. }else{
  21. dp[i + 1] = dp[i];
  22. }
  23. }
  24. }
  25. return dp[s.length()];
  26. }
  27. }

其中当前下标的取值只和前两个位置的取值,所以可以优化空间复制度
空间优化后的代码:

  1. class Solution {
  2. private int res = 0;
  3. public int numDecodings(String s) {
  4. if(s.charAt(0) == '0'){
  5. return 0;
  6. }
  7. // 空也是一种
  8. int pre = 1, cur = 1;
  9. for(int i = 1; i < s.length(); ++i){
  10. int tmp = cur;
  11. if(s.charAt(i) == '0'){
  12. if(s.charAt(i-1) == '1' || s.charAt(i-1) == '2'){
  13. cur = pre;
  14. }else{
  15. return 0;
  16. }
  17. }else{
  18. if(s.charAt(i-1) == '1' || (s.charAt(i-1) == '2' && s.charAt(i) <= '6')){
  19. cur = cur + pre;
  20. }else{
  21. cur = tmp;
  22. }
  23. }
  24. pre = tmp;
  25. }
  26. return cur;
  27. }
  28. }
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)