解码方法数问题

时间:2022-10-22 21:04:38

作者:Grey

原文地址:

博客园:解码方法数问题

CSDN:解码方法数问题

题目描述

一条包含字母 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" 在映射中并不等价)。

题目链接:LeetCode 91. Decode Ways

暴力递归解

定义递归函数int process(int i, char[] str)

递归含义表示:从 i 一直到最后,得到的解码方法数有多少

base case 是:

当 i 已经大于 str.length,说明之前的解码决策有问题,直接返回 0。

当 i 正好等于 str.length, 说明之前的决策正好有一种符合条件的情况,返回 1。

接下来就是普遍情况,即:i 小于 str.length, 此时,有如下几种决策情况

第一种情况

str[i]=='0',由于 0 无法解码成任何字符,也无法和后一个进行拼凑成一个字符的编码,所以,直接返回 0。表示决策无效。

第二种情况

str[i] == '1', 则可以有如下决策,首先,str[i]位置独立编码成一个字符,或者str[i]str[i+1]结合解码成一个字符。

第三种情况

str[i] == '2', 则可以有如下决策,首先,str[i]位置独立编码成一个字符,或者str[i]str[i+1]结合解码成一个字符,但是此时的str[i+1]的字符有条件,即:

i + 1 < str.length && str[i + 1] <= '6'

只有满足这个条件,str[i]才能和str[i+1]结合解码成一个字符。

第四种情况
str[i] > '2', 则str[i]只能单独解码成一个字符。

暴力解法的完整代码如下

class Solution {
    public static int numDecodings(String s) {
        if (null == s || s.length() < 1) {
            return 0;
        }
        char[] str = s.toCharArray();
        return process(0, str);

    }

    // 从i一直到最后,得到的解码数
    public static int process(int i, char[] str) {
        if (i > str.length) {
            return 0;
        }
        if (i == str.length) {
            return 1;
        }
        // i < str.length
        if (str[i] == '0') {
            return 0;
        }
        if (str[i] == '1') {
            int p1 = process(i + 1, str);
            int p2 = process(i + 2, str);
            return p1 + p2;
        }
        if (str[i] == '2') {
            int p1 = process(i + 1, str);
            if (i + 1 < str.length && str[i + 1] <= '6') {
                p1 += process(i + 2, str);
            }
            return p1;
        }
        return process(i + 1, str);
    }
}

直接超时

解码方法数问题

动态规划

有了上述暴力递归解法,可以直接改成动态规划解法,由于递归函数只有一个可变参数,所以定义一个一维数组即可装下所有可能性。

int[] dp = new int[str.length + 1];

dp[i]的含义和递归函数process(i,str)的含义一样,都是从 i 开始到最后,解码数量是多少。

由于暴力递归方法中,process(i,str)依赖process(i+1,str)process(i+2,str)

所以对于 dp 数组来说, dp[i]的值依赖dp[i+1]dp[i+2]决策的结果,

根据暴力递归方法中的 base case,可以得到 dp 的某些行列的初始值,然后根据递推公式进行递归,最后返回dp[0]就是结果。

动态规划解的完整代码如下

class Solution {
    public static int numDecodings(String s) {
        if (null == s || s.length() < 1) {
            return 0;
        }
        char[] str = s.toCharArray();
        int[] dp = new int[str.length + 1];
        dp[str.length] = 1;
        for (int i = str.length - 1; i >= 0; i--) {
            if (str[i] == '0') {
                dp[i] = 0;
            } else {
                dp[i] = dp[i + 1];
                if (str[i] == '1' && i + 1 < str.length) {
                    dp[i] = dp[i] + dp[i + 2];
                } else if (str[i] == '2' && i + 1 < str.length && str[i + 1] <= '6') {
                    dp[i] += dp[i + 2];
                }
            }
        }
        return dp[0];
    }
}

更多

算法和数据结构笔记