[动态规划] (四) LeetCode 91.解码方法

[动态规划] (四) LeetCode 91.解码方法

91. 解码方法

image-20231103192610042

题目解析

(1) 对字母A - Z进行编码1-26

(2)11106可以解码为1-1-10-6或者11-10-6, 但是11-1-06不能解码

(3) 0n不能解码

(4) 字符串非空,返回解码方法的总数

解题思路
状态表示

dp[i]:以i为结尾,的解码方法

状态转移方程

1.单独解码

  • dp[i]与dp[i-1]分别解码:s[i]解码成功,即加上dp[i-1];解码失败,则这种方法以及之前的解码方法dp[i-1]是错误的,方法数0

2.组合解码

  • dp[i]与dp[i-1]组合:s[i-1] * 10 + s[i],解码成功,即加上dp[i-2];解码失败,则到dp[i-2]的方法是错误的,方法数0
初始化和填表顺序
  • 初始化

我们使用了i-1i-2的值,所以初始化dp[0]和dp[1]。

dp[0]与s[0]有关

dp[1]与s[1]有关,还与s[0]与s[1]的组合有关

dp[0] = s[0] != '0';
if(dp[1] != '0') dp += dp[0];
if(dp[0] != '0' && dp[1] != '0') dp[1] += dp[0];
int sum = ((dp[0] - '0') * 10 + (dp[1] - '0'));
if(sum >= 10 && sum <= 26) dp[1] += 1;
  • 填表顺序

从左往右填表

返回值

返回n-1位置即可,同状态表示

代码实现
class Solution {
public:
    int numDecodings(string s) {
        //构建dp数组
        int n = s.size();
        vector<int> dp(n);
        //初始化
        if(s[0] != '0') dp[0] = 1;//单独解码
        if(n == 1) return dp[0];

        if(s[0] != '0' && s[1] != '0') dp[1] += dp[0];//单独解码
        int sum = (s[0] - '0') * 10 + s[1] - '0';
        if(sum >= 10 && sum <= 26) dp[1]++;//组合解码

        //填表
        for(int i = 2; i < n; i++)
        {
            //情况1
            if(s[i] != '0') dp[i] += dp[i-1];
            //情况2
            sum = (s[i-1] - '0') * 10 + s[i] - '0';
            if(sum >= 10 && sum <= 26) dp[i] += dp[i-2];
        }
        //返回结果
        return dp[n-1];
    }
};

image-20231103194639683

总结

细节1:字符串中数字进行±*/需要减一个字符0。

细节2:数据范围,字符串长度为1时直接返回dp[0]

细节3:初始化dp[1]时的代码与填表时的代码高度重合,我们可以进行优化

优化方法

1.将申请的空间扩大一位,将填表的下标向后推一位。

2.dp[0]初始化为1,dp[0]为我们虚构出来的一位;因为我们想要使i=2,dp[i]初始化正确,会访问到dp[i-2]。如果dp[0]为0,在计算组合的情况时,就会少加一次dp[i-2]。

3.因为我们把申请的空间dp,填表下标向后推一位,访问字符串s的下标得前进一位,则循环中s[i]的i都得减1

4.将填表的下标向后推一位,返回值也得向后推一位,即dp[n]。

优化代码
class Solution {
public:
    int numDecodings(string s) {
        //优化代码
        int n = s.size();
        vector<int> dp(n+1);
        dp[0] = 1;
        dp[1] = s[0] != '0';
        if(n == 1) return dp[1];
        for(int i = 2; i <= n; i++)
        {
            if(s[i-1] != '0') dp[i] += dp[i-1];
            int sum = ((s[i-2] - '0') * 10) + (s[i-1] - '0');
            if(sum >= 10 && sum <= 26) dp[i] += dp[i-2];
        }
        return dp[n];
    }
};

image-20231103195008881

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值