力扣 639. 解码方法 II

题目来源:https://leetcode-cn.com/problems/decode-ways-ii/

大致题意:

给定一个由数字 0 - 9 和 * 号(可以变成 1-9 的任意数字)组成的字符串,解码回由字母 A - Z 构成的字符串,其中 A 对应 1 … Z 对应 26。于是相邻的数字可以拆开解码,也可以在小于 26 时合并解码,这样就有了多种解码方法。求出解码方法的个数

思路

既然知道了解码可以单独或者两个,于是对于当前的字符 c,可以选择直接解码,也可以选择与上一个字符组合解码。

动态规划

因为有 * 号的存在,所以字符的解码方法可能有多种,并不唯一,分析可知:

  • 若当前字符单独解码:在当前字符等于 * 号时,可以有 9 种可能性;若当前字符不等于 * ,那么只要它不为 0,就会对应 1 种唯一的解码
  • 若当前字符与上一个字符合并解码:若两个字符都为 *,那么有 11 - 19 和 21 - 26 共 15 种解码方法;若前一个字符为 ,那么在当前字符小于等于 6 时, 号可以为 1 或 2,即 2 种解码方法,而若当前字符大于 6,则 * 号 只能为 1;若当前字符为 *,那么在上一个字符为 1 时, * 号可以为 1 - 9,共 9 种解码,而若为 2,则 * 号 可以为 1 - 6,共 6 种解码,大于 2 时,没有可用解码方法

于是使用 f[i] 存下首位到第 i 位有的解码方法,那么:

  • 若当前字符单独解码的方法有 x 种,f[i] 就等于 f[i - 1] * x
  • 若当前字符与前一个字符合并解码的方法有 y 种,那么 f[i] 可以加上 f[i - 2] * y
  • 于是,在当前字符不是首字符的时候就有 f[i] = f[i - 1] * x + f[i - 2] * y
  • 再加上边界初始化, f[0] = 1, 即长度为 0 的空串只能返回空串,一种解码;f[-1] = 0,即长度 -1,不存在

代码:

public static final int MOD = 1000000007;
    public int numDecodings(String s) {
        int n = s.length();
        // 使用三个变量存下 f_i-2 f_i-1 f_i,节约内存
        // 用 long 防止溢出
        long pre = 0;
        long medium = 1;
        long last = 0;
        // 遍历字符串,动态规划
        for (int i = 0; i < n; i++) {
            last = medium * decodeOneChar(s.charAt(i)) % MOD;
            if (i != 0) {
                last = (last + pre * decodeTwoChar(s.charAt(i-1), s.charAt(i)) % MOD ) % MOD;
            }
            pre = medium;
            medium = last;
        }

        return (int)last;
    }

    // 考虑对一个字符的解码
    public int decodeOneChar(char c) {
        if (c == '0') {
            return 0;
        }
        return c == '*' ? 9 : 1;
    }

    // 考虑对两个字符的解码
    public int decodeTwoChar(char c1, char c2) {
        if (c1 == '0') {
            return 0;
        }
        if (c1 == '*') {
            if (c2 == '*') {
                return 15;
            } else {
                return c2 <= '6' ? 2 : 1;
            }
        }
        if (c2 == '*') {
            if (c1 == '1') {
                return 9;
            } else if (c1 == '2') {
                return 6;
            } else {
                return 0;
            }
        }
        // 都为常数
        return ((c1 - '0') * 10 + (c2 - '0')) <= 26 ? 1: 0;

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值