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.
难度:90. 非常不错的一道一维DP题目,参考了网上的做法。
看到这种求数量的,我们很容易想到动态规划来存储前面信息,然后迭代得到最后结果。
我们维护的量res[i]是表示前i个数字有多少种解析的方式,接下来来想想递归式,有两种方式:第一种新加进来的数字不然就是自己比较表示一个字符,那么解析的方式有res[i-1]种,第二种就是新加进来的数字和前一个数字凑成一个字符,解析的方式有res[i-2]种(因为上一个字符和自己凑成了一个)。当然这里要判断前面说的两种情况能否凑成一个字符,也就是范围的判断,如果可以才有对应的解析方式,如果不行,那么就是0。最终结果就是把这两种情况对应的解析方式相加。这里可以把范围分成几个区间:
(1)00, 30, 40...90:res[i]=0(无法解析,没有可行解析方式);
(2)10, 20:res[i]=res[i-2](只有第二种情况成立);
(3)11-19, 21-26:res[i]=res[i-1]+res[i-2](两种情况都可行);
(4)01-09, 27-99:res[i]=res[i-1](只有第一种情况可行);
递推式就是按照上面的规则来得到,接下来我们只要进行一遍扫描,然后依次得到维护量就可以了,算法的时间复杂度是O(n)。空间上可以看出我们每次只需要前两位的历史信息,所以只需要维护三个变量然后迭代赋值就可以了,所以空间复杂度是O(1)。
注意一开始的初始值
1 public class Solution { 2 public int numDecodings(String s) { 3 if (s==null || s.length()==0 || s.charAt(0)=='0') { 4 return 0; 5 } 6 int num1 = 1; 7 int num2 = 1; 8 int num3 = 1; 9 for (int i=1; i<s.length(); i++) { 10 if (s.charAt(i) == '0') { 11 if (s.charAt(i-1) == '1' || s.charAt(i-1) == '2') { 12 num3 = num1; 13 } 14 else return 0; 15 } 16 else { 17 if (s.charAt(i-1) == '0' || s.charAt(i-1) >= '3') { 18 num3 = num2; 19 } 20 else if (s.charAt(i-1)=='2' && (s.charAt(i) =='7'||s.charAt(i)=='8'||s.charAt(i)=='9')) { 21 num3 = num2; 22 } 23 else num3 = num2 + num1; 24 } 25 num1 = num2; 26 num2 = num3; 27 } 28 return num2; 29 } 30 }
Best approach:
1 public class Solution { 2 public int numDecodings(String s) { 3 if(s == null || s.length() == 0 || s.charAt(0) == '0') { 4 return 0; 5 } 6 int n = s.length(); 7 int[] dp = new int[n+1]; 8 dp[0] = 1; 9 dp[1] = 1; 10 for(int i = 2; i <= n; i++) { 11 int first = Integer.valueOf(s.substring(i-1, i)); 12 int second = Integer.valueOf(s.substring(i-2, i)); 13 if(first == 0 && second != 10 && second != 20) { 14 // this check is optional, not necessary to have, but easy to understand 15 return 0; 16 } 17 if(first >= 1 && first <= 9) { 18 dp[i] += dp[i-1]; 19 } 20 if(second >= 10 && second <= 26) { 21 dp[i] += dp[i-2]; 22 } 23 } 24 return dp[n]; 25 } 26 }
optimize:
1 public class Solution { 2 public int numDecodings(String s) { 3 if(s == null || s.length() == 0) { 4 return 0; 5 } 6 7 int dp0 = 1; 8 int dp1 = s.charAt(0) != '0' ? 1 : 0; 9 int dp2 = 0; 10 11 for(int i = 2; i <= s.length(); i++) { 12 int first = Integer.valueOf(s.substring(i-1, i)); 13 int second = Integer.valueOf(s.substring(i-2, i)); 14 15 if(first >= 1 && first <= 9) { 16 dp2 += dp1; 17 } 18 if(second >= 10 && second <= 26) { 19 dp2 += dp0; 20 } 21 dp0 = dp1; 22 dp1 = dp2; 23 dp2 = 0; 24 } 25 return dp1; 26 } 27 }