动态规划——解码方法

题目链接

leetcode在线oj题——解码方法

题目描述

一条包含字母 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 = “06”
输出:0
解释:“06” 无法映射到 “F” ,因为存在前导零(“6” 和 “06” 并不等价)。

题目提示

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

解题思路

这道题是一个经典的动态规划问题,因此我们首先要考虑状态转移方程

状态转移方程

拿到字符串的每一个字符非常繁琐,我们先将字符串转换为字符数组

char[] chs = s.toCharArray();

紧接着,我们定义和字符数组长度相同的dp数组,存放i位置的编码数量

int[] dp = new int[n];

dp[i]代表了只考虑数组的前i个位置的编码数量有多少,我们可以按照爬楼梯的思想,将其分为两部分考虑,一部分是chs[i]单独解码,另一种是chs[i - 1]和chs[i - 2]两个一起解码

在这里插入图片描述

初始化dp数组

由于需要dp[i]和dp[i - 1],因此我们需要初始化dp[0]和dp[1],这样就可以从dp[2]开始进行状态转移,否则的话会出现数组越界

dp[0]显然就是chs[0]是否能够单独解码,判断其是否大于等于1,小于等于9

而如果字符串只有一位字符,那么直接返回即可

if(chs[0] != '0'){
    
    
   dp[0] = 1;
}

if(n == 1){
    
    
    return dp[0];
}

而dp[1]则有两种情况,如果chs[0]和chs[1]都能单独解码成功,则加一,如果chs[0] 和 chs[1]能组合解码成功,则再加一

int num1 = (chs[0] - '0') * 10 + (chs[1] - '0');
if(chs[0] != '0' && chs[1] != '0'){
    
    
    if(num1 >= 10 && num1 <= 26){
    
    
        dp[1] = 2;
    } else {
    
    
        dp[1] = 1;
    }
} else {
    
    
    if(num1 >= 10 && num1 <= 26){
    
    
        dp[1] = 1;
    } else {
    
    
        dp[1] = 0;
    }
}

最后直接返回dp[n - 1]即可

完整代码

package week15;

public class Solution1 {
    
    
    public int numDecodings(String s) {
    
    
        char[] chs = s.toCharArray();
        int n = chs.length;
        int[] dp = new int[n];

        //初始化
        if(chs[0] != '0'){
    
    
            dp[0] = 1;
        }

        if(n == 1){
    
    
            return dp[0];
        }

        int num1 = (chs[0] - '0') * 10 + (chs[1] - '0');
        if(chs[0] != '0' && chs[1] != '0'){
    
    

            if(num1 >= 10 && num1 <= 26){
    
    
                dp[1] = 2;
            } else {
    
    
                dp[1] = 1;
            }
        } else {
    
    
            if(num1 >= 10 && num1 <= 26){
    
    
                dp[1] = 1;
            } else {
    
    
                dp[1] = 0;
            }
        }

        //状态转移
        for(int i = 2; i < n; i++){
    
    
            if(chs[i] != '0'){
    
    
                dp[i] += dp[i - 1];
            }

            int num2 = (chs[i - 1] - '0') * 10 + (chs[i] - '0');
            if(num2 >= 10 && num2 <= 26){
    
    
                dp[i] += dp[i - 2];
            }
        }

        return dp[n - 1];
    }
}

优化代码

由于我们至少需要初始化dp数组的前两位,而代码中初始化dp[1]时候的代码和后面的迭代代码的冗余度非常高,所以我们可以将dp数组增加一位

这一位称之为辅助结点,只要确保后续的迭代是正确的值即可

当dp[0]是1时候,后续迭代是正确的,而dp[1]的初始化就是判断chs[0]是否能单独解码成功

由于我们前面增加了一位,因此后续的迭代中所有判断chs[i]的地方都换成chs[i - 1],最后返回dp[n]即可

优化后代码

package week15;

public class Solution2 {
    
    
    public int numDecodings(String s) {
    
    
        char[] chs = s.toCharArray();
        int n = chs.length;
        int[] dp = new int[n + 1];

        dp[0] = 1;
        //初始化
        if(chs[0] != '0'){
    
    
            dp[1] = 1;
        }

        //状态转移
        for(int i = 2; i <= n; i++){
    
    
            if(chs[i - 1] != '0'){
    
    
                dp[i] += dp[i - 1];
            }

            int num2 = (chs[i - 2] - '0') * 10 + (chs[i - 1] - '0');
            if(num2 >= 10 && num2 <= 26){
    
    
                dp[i] += dp[i - 2];
            }
        }

        return dp[n];
    }
}

猜你喜欢

转载自blog.csdn.net/m0_60867520/article/details/131469058