leetcode day06 动态规划之解码方法I和II

91.解码方法

该题类似于爬楼梯

方法一:动态规划

对于给定的字符串 s,设它的长度为 n,其中的字符从左到右依次为 s[1],s[2],⋯,s[n]。我们可以使用动态规划的方法计算出字符串 s 的解码方法数。

具体地,设 f i 表示字符串 s 的前 i 个字符 s[1..i] 的解码方法数。在进行状态转移时,我们可以考虑最后一次解码使用了 s 中的哪些字符,那么会有下面的两种情况:

*  第一种情况是我们使用了一个字符,即 s[i] 进行解码,那么只要 s[i] !=0,它就可以被解码成 A∼I 中的某个字母。由于剩余的前 i−1 个字符的解码方法数为 f i−1​,因此我们可以写出状态转移方程:

                                                         fi​=fi−1​,其中 s[i] !=0

*  第二种情况是我们使用了两个字符,即 s[i−1] 和 s[i] 进行编码。与第一种情况类似,s[i−1] 不能等于 0,并且 s[i−1] 和 s[i] 组成的整数必须小于等于 26,这样它们就可以被解码成 J∼Z 中的某个字母。由于剩余的前 i−2 个字符的解码方法数为 f i−2 ,因此我们可以写出状态转移方程:

                                        fi​=fi−2​,其中 s[i−1] !=0 并且 10⋅s[i−1]+s[i]≤26

 需要注意的是,只有当 i>1 时才能进行转移,否则 s[i−1] 不存在。

将上面的两种状态转移方程在对应的条件满足时进行累加,即可得到 fi​ 的值。在动态规划完成后,最终的答案即为 fn​。

注意:动态规划的边界条件为: 

                                                               f0 = 1

即空字符串可以有 1 种解码方法,解码出一个空字符串。

class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        vector<int> f(n+1);
        f[0] = 1; //空字符串
        for(int i=1;i<=n;i++){
            if(s[i-1]!='0'){
                f[i] += f[i-1];
            }
            if(i>1 && s[i-2]!='0' && ((s[i-2]-'0')*10 + (s[i-1]-'0')<=26 )){
                f[i] += f[i-2];
            }
        }
        return f[n];
    }
};

639  解码方法II

这里要分多种情况讨论。

 

因为这里总共两类,且每一类的情况很多,需要重复书写的地方也很多,故而可以采用两个函数,分别返回int值,这样就会显得简洁明了。而且便于更改。

class Solution{
private:
    static constexpr int mod = 1000000007;
    int check1(char ch){
        if(ch=='0'){
            return 0;
        }
        return ch == '*'?9:1;
    }
    int check2(char c0,char c1){
        if (c0 == '*' && c1 == '*') {
            return 15;
        }
        if (c0 == '*') {
            return c1 <= '6' ? 2 : 1;
        }
        if (c1 == '*') {
            if (c0 == '1') {
                return 9;
            }
            if (c0 == '2') {
                return 6;
            }
            return 0;
        }
        return c0 != '0' && (c0 - '0') * 10 + (c1 - '0') <= 26;
    }
public:
    int numDecodings(string s){
        int n = s.size();
        int a = 0, b = 1, c = 0;
        // c 表示 f[i], b = f[i-1], a=f[i-2];
        for (int i = 1; i <= n; ++i) {
            c = (long long)b * check1(s[i - 1]) % mod;
            if (i > 1) {
                c = (c + (long long)a * check2(s[i - 2], s[i - 1])) % mod;
            }
            a = b;
            b = c;
        }
        return c;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值