一、题目描述
题目描述:有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。现在给一串数字,返回有多少种可能的译码结果
二、算法1(暴力深搜)
解题思路
- 暴力枚举每一种翻译方式,最后统计方案数
- 每次递归考虑两种情况,当前位的数自成一位与下一位数结合,当然,考虑只有当前一位时不能为0,考虑两位数时数值应该是10到26的范围
代码实现(C++11)
class Solution { public: int solve(string nums) { int n = nums.size(); return dfs(nums, 0, n); } int dfs(string& nums, int p, int n) { if (p == n) return 1; // 找到一种合法结果 int ans = 0; // 若当前位不为'0', 自成一类 if (nums[p] != '0') ans += dfs(nums, p + 1, n); // 考虑当前位和下一位结合为一类 if (p + 1 < n) { int digit = (nums[p] - '0') * 10 + (nums[p + 1] - '0'); if (digit >= 10 && digit <= 26) ans += dfs(nums, p + 2, n); } return ans; } };
时间复杂度:, n为给定字符串的长度,最坏情况下每次分两条路递归,时间复杂度指数级
空间复杂度:,递归深度为字符串长度n
优化(记忆化搜索)
暴力搜索的时间复杂度高在存在很多重复的分支,因此我们可以使用记忆化搜索,记录已知状态的结果,当重复某一已知步骤时,直接从备忘数组memo中取出结果返回即可,这里memo定义为从当前遍历到的位置到结尾的合法结果数
class Solution { public: int solve(string nums) { int n = nums.size(); vector<int> memo(n, -1); // memo[i]表示从当前位置到结尾的合法方案数 dfs(nums, 0, n, memo); return memo[0]; } int dfs(string& nums, int p, int n, vector<int>& memo) { if (p == n) return 1; // 找到一种合法结果 if (memo[p] != -1) return memo[p]; int ans = 0; // 若当前位不为'0', 自成一类 if (nums[p] != '0') ans += dfs(nums, p + 1, n, memo); // 考虑当前位和下一位结合为一类 if (p + 1 < n) { int digit = (nums[p] - '0') * 10 + (nums[p + 1] - '0'); if (digit >= 10 && digit <= 26) ans += dfs(nums, p + 2, n, memo); } return memo[p] = ans; } };
时间复杂度:, n是字符串长度,时间复杂度为dp相近
空间复杂度:, 递归栈空间和备忘数组占用空间
三、算法2(动态规划)
解题思路
- 求方案数问题一般都涉及到动态规划,要得到整个数字的翻译结果,可以从更小规模的数字推出,因此可以联想到线性动规
- 定义数组f[i]表示数字nums[0...i]能被翻译成多少种结果,对于当前字符nums[i],最多只有两种情况,若能自成一位,则方案数为f[i - 1],若能和前面一位结合成合法的两位数,则方案数为f[i - 2],若两种都满足则方案数为f[i - 1] + f[i - 2]
故状态转移方程为:
其中B表示nums[i]表示的数, AB表示nums[i - 1]和nums[i]组合的数
代码实现(C++11)
class Solution { public: int solve(string nums) { int n = nums.size(); vector<int> f(n + 1); // 预处理f[0]和f[1], 可以避免边界处理 f[0] = 1, f[1] = nums[0] != '0'; for(int i = 2; i <= n; i++) { if(nums[i - 1] != '0') f[i] = f[i - 1]; // 若当前位不为'0',可单独翻译 int digit = (nums[i - 2] - '0') * 10 + (nums[i - 1] - '0'); if (10 <= digit && digit <= 26) { // 当前位与上一位数组合(必须是合法的两位数字) f[i] += f[i - 2]; } } return f[n]; } };
时间复杂度:, n为字符串长度,遍历一次数组,时间复杂度为O(n)
空间复杂度:, 辅助数组使用的空间是O(n)的