盛大游戏笔试

leetcode 91. Decode Ways

一、问题描述

一条由A-Z字母组成的消息使用以下映射编码成数字:
    'A' -> 1
    'B' -> 2
    ...
    'Z' -> 26
给定一个只包含数字的非空字符串,确定解码方法的总数。
【举例】
<例 1>
    输入: "12"
    输出: 2
    解释: "AB" (1 2) or "L" (12).
<例 2>
    输入: "226"
    输出: 3
    解释: "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

二、解题思路

dp[i]为长度为i的子串的总的编码方式
    则初始状态 dp[0]=0 ; dp[1]=1
    状态转移方程为:
    dp[i] =
          {
             dp[i-1]+dp[i-2] i>2&&( s[i-2]=='1'||s[i-2]=='2'&&s[i-1]<='6' )
             dp[i-1]+1       i==2
          }

  • dp[i]=dp[i-1] 状态表示单个数字有效可编码时,长度为i的子串编码方式数继承长度为i-1的编码方式数
  • dp[i]=dp[i-1]+dp[i-2] 状态表示两个数字有效可编码时,长度为i的子串编码方式数为长度为i-1编码方式数与长度为i-2编码方式数之和

动态规划

[LeetCode] Decode Ways 解码方法

 

这道题要求解码方法,跟之前那道 Climbing Stairs 爬梯子问题 非常的相似,但是还有一些其他的限制条件,比如说一位数时不能为0,两位数不能大于26,其十位上的数也不能为0,出去这些限制条件,根爬梯子基本没啥区别,也勉强算特殊的斐波那契数列,当然需要用动态规划Dynamci Programming来解。建立一位dp数组,长度比输入数组长多多2,全部初始化为1,因为斐波那契数列的前两项也为1,然后从第三个数开始更新,对应数组的第一个数。对每个数组首先判断其是否为0,若是将改为dp赋0,若不是,赋上一个dp值,此时相当如加上了dp[i - 1], 然后看数组前一位是否存在,如果存在且满足前一位不是0,且和当前为一起组成的两位数不大于26,则当前dp值加上dp[i - 2], 至此可以看出来跟斐波那契数组的递推式一样,代码如下:

 

C++ 解法一:

复制代码

class Solution {
public:
    int numDecodings(string s) {
        if (s.empty() || (s.size() > 1 && s[0] == '0')) return 0;
        vector dp(s.size() + 1, 0);
        dp[0] = 1;
        for (int i = 1; i < dp.size(); ++i) {
            dp[i] = (s[i - 1] == '0') ? 0 : dp[i - 1];
            if (i > 1 && (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))) {
                dp[i] += dp[i - 2];
            }
        }
        return dp.back();
    }
};

复制代码

 

Java 解法一:

复制代码

public class Solution {
    public int numDecodings(String s) {
        if (s.isEmpty() || (s.length() > 1 && s.charAt(0) == '0')) return 0;
        int[] dp = new int[s.length() + 1];
        dp[0] = 1;
        for (int i = 1; i < dp.length; ++i) {
            dp[i] = (s.charAt(i - 1) == '0') ? 0 : dp[i - 1];
            if (i > 1 && (s.charAt(i - 2) == '1' || (s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6'))) {
                dp[i] += dp[i - 2];
            }
        }
        return dp[s.length()];
    }
}

复制代码

 

下面这种方法跟上面的方法的思路一样,只是写法略有不同:

 

C++ 解法二:

复制代码

class Solution {
public:
    int numDecodings(string s) {
        if (s.empty()) return 0;
        vector dp(s.size() + 1, 0);
        dp[0] = 1;
        for (int i = 1; i < dp.size(); ++i) {
            if (s[i - 1] != '0') dp[i] += dp[i - 1];
            if (i >= 2 && s.substr(i - 2, 2) <= "26" && s.substr(i - 2, 2) >= "10") {
                dp[i] += dp[i - 2];
            }
        }
        return dp.back();
    }
};

复制代码

 

我们再来看一种空间复杂度为O(1)的解法,我们用两个变量c1, c2来分别表示s[i-1]和s[i-2]的解码方法,然后我们从i=1开始遍历,也就是字符串的第二个字符,我们判断如果当前字符为'0',说明当前字符不能单独拆分出来,只能和前一个字符一起,我们先将c1赋为0,然后我们看前面的字符,如果前面的字符是1或者2时,我们就可以更新c1 = c1 + c2,然后c2 = c1 - c2,其实c2赋值为之前的c1,如果不满足这些条件的话,那么c2 = c1,参见代码如下:

 

C++ 解法三:

复制代码

class Solution {
public:
    int numDecodings(string s) {
        if (s.empty() || s.front() == '0') return 0;
        int c1 = 1, c2 = 1;
        for (int i = 1; i < s.size(); ++i) {
            if (s[i] == '0') c1 = 0;
            if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] <= '6')) {
                c1 = c1 + c2;
                c2 = c1 - c2;
            } else {
                c2 = c1;
            }
        }
        return c1;
    }
};

复制代码

 

参考资料:

https://discuss.leetcode.com/topic/7025/a-concise-dp-solution

https://discuss.leetcode.com/topic/6647/accepted-solution-to-decode-ways-no-need-to-take-care-of-0-case

你可能感兴趣的:(leetcode)