zoukankan      html  css  js  c++  java
  • 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;
        }
    };
  • 相关阅读:
    linux 删除某种规则命名的文件
    adb shell 出现 error :
    android 开发,多个线程共用一个handler
    android 开发上传图片遇到返回 FileNotFoundException
    mysql 的存储过程调试软件
    输入adb shell 时 提示error: more than one device and emulator
    高德开发 android 出现 key 鉴权失败
    android 中设置HttpURLConnection 超时并判断是否超时
    LINQ to SQL语句(3)之Count/Sum/Min/Max/Avg
    C# 如何判断数据是否为 NaN
  • 原文地址:https://www.cnblogs.com/lailailai/p/3599468.html
Copyright © 2011-2022 走看看