[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.
问题:对给定纯数字字符串解码,求解法的可能情况有多少种。
注:0 开头的字符串算作无效, 例如, "02" 无法解码。
也是一道蛮典型的 DP 问题。
总的解码个数,等于对第一个数字单独解码的情况,加上对前两个数字一起解码的情况。
利用 vector<int> v 记录中间计算结果。 v[i] 表示 s[i...n] 的解码个数。
1 vector<int> v; 2 3 int decode(string s, int p){ 4 5 if (v[p] != -1) { 6 return v[p]; 7 } 8 9 if (s[0] == '0') { 10 v[p] = 0; 11 return 0; 12 } 13 14 if (s.size() == 1) { 15 v[p] = 1; 16 return 1; 17 } 18 19 if (s.size() == 2) { 20 int tmp = 0; 21 if (s[1] != '0') { 22 tmp++; 23 } 24 25 if (stoi(s) <= 26) { 26 tmp++; 27 } 28 v[p] = tmp; 29 return tmp; 30 } 31 32 33 int n1 = 0; 34 if (s[1] != '0') { 35 n1 = decode(s.substr(1), p+1); 36 } 37 38 int n2 = 0; 39 if (stoi(s.substr(0, 2)) <= 26) { 40 n2 = decode(s.substr(2), p+2); 41 } 42 43 int res = n1 + n2; 44 45 v[p] = res; 46 return res; 47 } 48 49 50 int numDecodings(string s) { 51 vector<int> tmp(s.size() , -1); 52 53 v = tmp; 54 55 if(s.size() == 0){ 56 return 0; 57 } 58 59 int res = decode(s, 0); 60 61 return res; 62 }
这道题解了两次,第一次没有解出来,放在一边,做了其他 DP 题目后,第二次再做,觉得顺畅了许多。
第一次解的时候,思路是分治(Divide and conquer)。
分治主要有三个步骤:
分(Divide) :将原问题分割为类型相同的子问题。
治(Conquer) :分别解决各个子问题
整合(Combine) : 将各个子问题结果整合,得到原问题解。
算法: 将密码字符串从中间分割,分别求解两个子问题,并求解中间不分割的情况,整合三个结果,得到原问题的解。
但是,求解中间不分割情况处理起来比较复杂,就没有继续下去。
第二次解的时候,已经做了一些 DP 问题,思路就往 DP 方向想。
动态规划(Dynamic Programming) 的关键条件有两个:
重叠子问题(overlapping sub-problems) : 子问题和原问题是同类型,解法可重用。这点和分治的前两个步骤类似。
最优子结构(optimal substructure) :子问题的结果,可以比较直接地得到原问题的结果。
对于字符串问题,
若采用分治思路,左右子问题都必须求解,并且还要考虑中间不分割的情况,算法容易变得复杂。
若采用 DP 思路,从左往右一直扫过去,免去了分治中求整合的那一步骤,时间复杂度和分治应该差不多,不过算法实现更加简洁。