LeetCode Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 Given an encoded message containing digits, determine the total number of ways to decode it. For example, Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). The number of ways decoding "12" is 2.

class Solution {

private:

    // used by dfs only

    int* memo;

    const char* str;

public:

    // 1 .dfs using a memo

    int _numDecodings(string s) {

        str = s.c_str();

        memo = new int[s.length()];

        memset(memo, -1, sizeof(int) * s.length());

        

        int ways = dfs(str, str + s.length() - 1);

        

        delete[] memo;

        return ways;

    }

    

    // 2. dynamic programming

    int numDecodings(string s) {

        const char* str = s.c_str();

        int len = s.length();

        if (len == 0) return 0;

        int ways = 0;

        

        int (*dp)[2] = new int[len + 2][2];

        dp[0][0] = dp[0][1] = dp[1][1] = 0;

        dp[1][0] = 1;

        

        for (int i=0; i<len; i++) {

            int d1 = str[i] - '0', d2 = 0;

            int prev = dp[i+1][0] + dp[i][1];

            bool d1_valid = (d1>=1 && d1<=26);

            dp[i+2][0] = d1_valid ? prev : 0;

            

            if (i+1 < len && d1_valid) {

                d2 = d1 * 10 + str[i+1] - '0';

                dp[i+2][1] = (d2 >=1 && d2 <= 26) ? prev : 0;

            } else {

                dp[i+2][1] = 0;

            }

        }

        ways = dp[len - 1 + 2][0] + dp[len - 2 + 2][1];

        delete[] dp;

        return ways;

    }

    

    int dfs(const char* cur, const char* last) {

        if (cur > last) return 0;

        int d1 = *cur - '0';

        if (d1 < 1 || d1 > 26) return 0;

        int midx = cur - str;

        if (memo[midx] != -1) return memo[midx];

        

        

        int d2 = 0;

        

        if (cur != last) {

            d2 = d1*10 + *(cur+1)-'0';

        }

        int c1 = 0, c2 = 0;

        if (cur == last) {       // last one digit

            c1 = 1;

        } else {                        // other cases

            c1 = dfs(cur + 1, last);

        }

        if (d2 > 26) {                  // invalid

            c2 = 0;

        } else if (cur + 1 ==  last) {  // last two digit

            c2 = 1;

        } else {                        // other cases

            c2 = dfs(cur + 2, last);

        }

        

        memo[midx] = c1 + c2;

        return memo[midx];

    }

};

1. 首先可以用dfs进行穷举搜索,但这跟刚开始学编程语言那会儿用递归函数计算斐波拉契数列一样有很多步重复,因此可以使用一个数组来存储这些已有的计算结果

2. 往往能够用方式1进行解决的问题可以采用动态规划解决,那就用呗!

 

用dp[i+2][0]表示从字符串开始到第i-1个字符(i从0开始到str.len-1),且把第i位作为一个字母看待时的合法的可能情况总数。

用dp[i+2][1]表示从字符串开始到第i-1个字符(i从0开始),且把第i位,第i+1位合起来作为一个字母看待时合法的可能情况总数。

而前面的[0, i-1]个字符可以通过子串[0,i-2] 连接[i-1](将一个数字作为字母代码看待) 或者 [0, i-3] 连接 [i-2, i-1](将两个数字作为字母代码看待)得到。

而这两种情况的数目为dp[i-1 + 2][0] + dp[i-2 + 2][1]

 

现在只要判断第i作为一个字母的代码是否合法和[i,i+1]位合起来作为一个字母的代码是否合法了,如果合法则情况数目为dp[i-1 + 2][0] + dp[i-2 + 2][1],如果不合法则置为零。

 

根据算法导论上的说法,如果一个问题的解可以用它的最优子问题解表示那么就可以采用动态规划去解决。memo数组中存储的其实就是一些问题的最优子问题解,

dp数组中存储的也是这样。

 

第二轮dp可以写得很简单:

class Solution {

public:

    int numDecodings(string s) {

        int len = s.size();

        if (len < 1) {

            return 0;

        }

        vector<int> dp(len + 1, 0);

        dp[0] = 1;

        dp[1] = s[0] != '0';

        for (int i=2; i<=len; i++) {

            int d0 = s[i-2] - '0';

            int d1 = s[i-1] - '0';

            int sum= d0 * 10 + d1;

            dp[i] += dp[i-1] * (d1 >= 1 && d1 <= 9);

            dp[i] += dp[i-2] * (d0 != 0 && sum >= 1 && sum <= 26);

        }

        return dp[len];

    }

};

 

 

你可能感兴趣的:(LeetCode)