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];
    }
};

 由于每次迭代都只用到了dp数组中相邻两个位置的数据,而已进一步化简为:

class Solution {
public:
    int numDecodings(string s) {
        int len = s.size();
        if (len < 1) {
            return 0;
        }
        
        int dp0 = 1;
        int dp1 = 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;
            int turn = 0;
            turn += dp1 * (d1 >= 1 && d1 <= 9);
            turn += dp0 * (d0 != 0 && sum >= 1 && sum <= 26);
            dp0 = dp1;
            dp1 = turn;
        }
        return dp1;
    }
};

 

 

posted @ 2014-03-13 22:22  卖程序的小歪  阅读(191)  评论(0编辑  收藏  举报