LeetCode 91. 解码方法

LeetCode 91. 解码方法 (Java 回溯+记忆化搜索、动态规划)

解题思路

小小炫耀一下!!
image.png
看到这题一开始想到的就是回溯
递归的结束条件为能遍历完整个字符串即解法可行,返回1
因为题目说明"06"与"6"并不相等,因此当我们遇到字符’0’时,就结束递归。
如果不是’0’就有两种情况前进1个字符或2个字符,因为单个字符一定能找到对应字母,所以先让其以每次扫描1字符的顺序递归
当进行每次扫描2字符时,先要判断下标是否越界,然后判断这两个字符组成的数字是否字母集合内即小等于26。

class Solution {
    public int numDecodings(String s) {
        char[] sca = s.toCharArray();
        return dfs(sca, 0);
    }

    public int dfs(char[] s, int index) {
        if (index >= s.length) {
            return 1;
        }
        int num = 0;
        if (s[index] != '0') {
            num += dfs(s, index + 1);
            if(index < s.length - 1){
                int total = (s[index] - '0') * 10 + (s[index + 1] - '0');
                if (total <= 26){
                    num += dfs(s, index + 2);
                }
            }
            
        }
        return num;
    }
}

但是单纯用回溯时间复杂度太高,很容易超出时间限制就比如我第一次提交那样。

  • 解法一

于是就参考别人的优化策略,用一个数组保存当前下标的解法的数目。
如果112355512,处理1 12 3…以及 11 2 3… 或者 1 1 2 3…如果我们知道下标3后面的有几种解法,那么是不是就不用重复三次3后面的递归,从而大大减少递归次数,既而减少算法的时间。

class Solution {
    public int numDecodings(String s) {
        char[] sca = s.toCharArray();
        int[] memory = new int[sca.length];
        return dfs(sca, 0, memory);
    }

    public int dfs(char[] s, int index, int[] mem) {
        if (index >= s.length) {
            return 1;
        }
        if (mem[index] > 0) {
            return mem[index];
        }else if (mem[index] < 0){
            return 0;
        }
        int num = 0;
        if (s[index] != '0') {
            num += dfs(s, index + 1, mem);
            if(index < s.length - 1){
                int total = (s[index] - '0') * 10 + (s[index + 1] - '0');
                if (total <= 26){
                    num += dfs(s, index + 2, mem);
                }
            }
            
        }
        mem[index] = num == 0 ? -1 : num;
        return num;
    }
}
  • 解法二

最后一种解法也是最快的解法:动态规划
动态规划即把问题从子序列一步步推算出完整序列的解法

比如:字符串长度为5,那就先假设长度为1求出dp[0],然后长度变为2,在根据dp[0]求出dp[1]…一直推演到长度为5,求出dp[4]即为最终的结果。

首先求状态转移方程:
当s[0]=0时,直接return 0;
当s[0]!=0时,令dp[0] = 1;
对于之后的每一个字符首先判断能不能自己有对应字母,若有dp[i] = dp[i-1];
在判断与i-1的元素相结合能不能有对应字母,若有dp[i]+=dp[i-2];相当于把i-1和i的元素看成一个整体A,若前i-2个元素有m种组合方式,那么再加上A这个整体并不使原有的组合方式改变,此时前i个元素的组合方式应该是m再加上整体A的组合方式。

如:1 2 3 有 3种组合方式:1 2 3、12 3、1 23.此时加上某个整体A(已经含有两个元素)变为:1 2 3 A还是只有3种组合方式 1 2 3 A、12 3 A、1 23 A;

class Solution {
    public int numDecodings(String s) {
        char[] sca = s.toCharArray();
        int[] dp = new int[sca.length];
        if (sca[0] != '0') {
            dp[0] = 1;
        }
        else {
            return 0;
        }
        for (int i = 1; i < sca.length; i++) {
            if (sca[i] >= '1' && sca[i] <= '9') {
                dp[i] = dp[i - 1];
            }
            if (sca[i-1] == '1' || sca[i-1] == '2' && sca[i] <= '6') {
                if (i >= 2) {
                    dp[i] += dp[i - 2];
                }else {
                    dp[i] ++;
                }
            }
        }
        return dp[sca.length - 1];
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值