[Leetcode]Dynamic Programming

Dynamic Programming

91. Decode Ways

91. Decode Ways

[Analysis]

此题目考虑使用动态规划的思想。思路如下,假设已知一个字符串可以decode为N种不同的字符组合,那么此时再加入一个新的数字进入这个字符串中又能构成多少种不同的字符组合。这是动态规划的基本思想,也就寻找一种状态转移方程。

设L为原始的由数字组成的字符串,令函数f(x)表示为在L中,从起点开始,长度为x的数字字符串能够表示为f(x)种字母字符串,L(x)表示为L字符串中第x个字符。
此时,如果L(x + 1)不是0,这意味着L(x + 1)可以单独decode,所以f(x) 至少会大于等于 f(x - 1),如果L(x + 1) 等于0,并且L(x)不等于1或则2,则此时L字符串无法被decode,直接输出0,否则f(x) = f(x - 1),意思就是说f(x)只能是x - 1个字符串中表示的所有字符串后面加一个L(x) + L(x + 1)。
如果L(x + 1)不是0,且L(x) L(x + 1)能够构成一个可以被decode的字符,则f(x) = f(x - 1) + f(x - 2),意思就是说,f(x)可以表示为长度为x - 1的字符串的所有组合加L(x), L(x + 1)和长度为x的字符加上L(x)。
最终代码如下。此处逻辑需要仔细思考。

class Solution {
private:
    bool isValid(int a, int b) {
        return a == '1' or (a == '2' and b <= '6');
    }

public:
    int numDecodings(string s) {
        if (s.length() == 0 || s[0] == '0') {
            return 0;
        }
        vector<int> states(s.length() + 1, 0);
        states[0] = 1;
        states[1] = 1;
        for (int i = 1; i < s.length(); i++) {
            if (s[i] == '0' and (s[i - 1] == '1' or s[i - 1] == '2')) {
                states[i + 1] = states[i - 1];
            } else if (s[i] == '0') {
                return 0;
            } else if (isValid(s[i - 1], s[i])) {
                states[i + 1] = states[i] + states[i - 1];
            } else {
                states[i + 1] = states[i];
            }
        }
        return states[s.length()];
    }
};

时间复杂度为O(n),空间复杂度为O(n)。

实际上,还存在一个更简单的想法。
如果L(x)大于0,则f(x)至少为f(x - 1);
如果L(x - 1)为1,则f(x)至少为f(x - 2);
如果L(x - 1)为2且L(x) 小于等于6,则f(x)至少为f(x - 2);
因此代码如下。

class Solution {
public:
    int numDecodings(string s) {
        if (s.length() == 0 || s[0] == '0') {
            return 0;
        }
        int e0 = 1, e1 = 0, e2 = 0;
        int f0, f1, f2;
        for (auto each : s) {
            f0 = (each > '0') * e0 + e1 + (each <= '6') * e2;
            f1 = (each == '1') * e0;
            f2 = (each == '2') * e0;
            e0 = f0;
            e1 = f1;
            e2 = f2;
        }
        return e0;
    }
};

时间复杂度为O(n), 空间复杂度为O(1)。

639. Decode Ways II

639. Decode Ways II

[Analysis]

这道题比第一题复杂得多了。原因在于出现了通配符*,这时如果还是用方法1就会比较麻烦。如果采用方法二,就相对简单许多。

具体代码如下,思路如上。需要注意的是*可以表示为9种不同的数字,所以可以创造9倍原先的decdoe结果数。

class Solution {
public:
    int numDecodings(string s) {
        int mod = 1000000007;
        if (s.length() == 0 || s[0] == '0') {
            return 0;
        }
        long long e0 = 1, e1 = 0, e2 = 0;
        long long f0, f1, f2;
        for (auto each : s) {
            if (each == '*') {
                f0 = 9 * e0 + 9 * e1 + 6 * e2;
                f1 = e0;
                f2 = e0;
            } else {
                f0 = (each > '0') * e0 + e1 + (each <= '6') * e2;
                f1 = (each == '1') * e0;
                f2 = (each == '2') * e0;
            }
            e0 = f0 % mod;
            e1 = f1;
            e2 = f2;
        }
        return e0;
    }
};

时间复杂度为O(n),空间复杂度为O(1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值