开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情
描述
有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。
现在给一串数字,返回有多少种可能的译码结果
数据范围:字符串长度满足 0 < n \le 900<n≤90
进阶:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
示例1
输入:
"12"
返回值:
2
说明:
2种可能的译码结果(”ab” 或”l”)
示例2
输入:
"31717126241541717"
返回值:
192
说明:
192种可能的译码结果
分析题目:能够译码的数字不会大于26,即有效的译码范围为 [1,26]。
动态规划
在手动翻译一串数字时,我们会对数字串的第一个数字寻找对应的字母,接着加入数字串的第二个数字,并且寻找其对应的字母,当然为了得到更多的译码结果我们会对第一个数字和第二个数字组成的数寻找对应的字母,不过此时这个数必须小于等于26,否则没有对应的字母,接着我们在前面的翻译结果下,加入数字串的第三个数字以及第四个数字,以此类推,直到完成所有的数字串。记录之前翻译的结果而对当前翻译有影响的过程即为典型的动态规划过程,因此方法一我们介绍动态规划。上面的分析使我们成功的将问题转换为动态规划的问题,对于一个动态规划问题,我们只需要从两个方面考虑,那就是找出问题之间的联系,以及记录答案。用一句话解释动态规划就是记住之前的结果。解决动态规划通常分为四个步骤:
- 问题拆解,找到问题之间的联系,即当前问题受到前面问题的影响
- 状态定义
- 转移方程推导
- 具体实现
解题思路
- 求方案数问题一般都涉及到动态规划,要得到整个数字的翻译结果,可以从更小规模的数字推出,因此可以联想到线性动规
- 定义数组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]组合的数
代码
class Solution {
public:
/**
* 解码
* @param nums string字符串 数字串
* @return int整型
*/
int solve(string nums) {
// write code here
int len=nums.size();
if(len==0 || nums[0]=='0')//排除掉空数字串以及以0开始的数字串
return 0;
vector<int> dp(len,0);//dp[i]用于存储到第i位数字的译码方案数,其中i=0,1,2...len-1
dp[0]=1;//i=0,第一位数结尾的译码方法数必为1
for(int i=1;i<len;i++)
{
if(nums[i] == '0')
{
if(nums[i-1] =='1'||nums[i-1] == '2')
{
if(i == 1) dp[i] = 1;//数字串以10或者20开头的情况
else dp[i] = dp[i-2];//数字串中存在10或者20的情况下,当前译码数等于后退两步的译码数
}
}
else if(nums[i - 1] == '1' || (nums[i - 1] == '2' && nums[i] >= '1' && nums[i] <= '6'))
{
if(i == 1) dp[i] = 2;//数字串开始不是10或者20的情况,dp[1]=2
else dp[i] = dp[i-1] + dp[i-2];
}
else
{
dp[i] = dp[i-1];
}
}
return dp[len-1];
}
};