【剑指offer-C++】JZ46:把数字翻译成字符串

【剑指offer-C++】JZ46:把数字翻译成字符串

题目描述

描述:有一种将字母编码成数字的方式:‘a’->1, ‘b->2’, … , ‘z->26’。

现在给一串数字,返回有多少种可能的译码结果。

数据范围:字符串长度满足 0<n≤90。

进阶:空间复杂度 O(n),时间复杂度 O(n)。

输入:"12"
返回值:2
说明:2种可能的译码结果(”ab” 或”l”)  
输入:"31717126241541717"
返回值:192
说明:192种可能的译码结果  

解题思路

把数字翻译成字符串:最直观的想法是,一开始想,首先使用dp[i]表示前i个字符串的翻译结果数,初始化均为1,因为字符串一个字符一个字符翻译则结果数为1,然后从第二个元素开始遍历,如果当前字符和前一个字符组成的数在10~26之间,则会出现两种翻译结果,一种是一个字符一个字符,另一种是当前字符和前一个字符,dp[i]=2*dp[i-1],反之则直接翻译,dp[i]=dp[i-1],最后返回dp[nums.size()-1]即可。(出错)

分析:但是上述这样想出错了,那么肯定是递推公式有些许问题,不过动态规划本身就有点复杂,错了也没关系,肯定是某些方面没有考虑全面,找找原因再接再厉好啦。根据上述描述,我们不妨将一个字符一个字符称为直接译码,当前字符和前一个字符称为组合译码,两种结果的翻译结果应该是dp[i]=dp[i-1]+dp[i-2],而不是dp[i]=2*dp[i-1],因为直接译码是dp[i]=dp[i-1],而组合译码是当前和前一个组合,于是是截止前前一个的结果,即dp[i]=dp[i-2]。同时,注意,字符串中可能存在数字0,而数字0是不能被编码的,所以不应该是10~26,而应该是排除10和20,得到11~19和21~26,因为10和20算了两种,分别是1和0还有10、2和0还有20,但是0不能被编码,要考虑这一点,10和20只有一种编码,即10和20,那么dp[i]=dp[i-2]。

总结:综上所述,一共有三种情况,根据当前字符和前一个字符的组合情况:第一种是10和20,则dp[i]=dp[i-2];第二种是11~19或者21~26,则dp[i]=dp[i-1]+dp[i-2];第三种是其他情况,则dp[i]=dp[i-1]。在终止条件中需要考虑如果当前字符为0而前一个字符不为1且不为2时需要返回0。代码如下,虽然有点冗余,但是十分清晰。(成功不是一蹴而就的,进一寸有进一寸的欢喜)

int solve(string nums) 
{
     //空字符串
     if(nums.size()==0)
        return 0;
     //字符串0
     if(nums=="0")
        return 0;
     //字符串10或者20
     if(nums=="10"||nums=="20")
        return 1;
     //当0前面不是1或者2时无法译码
     for(int i=1;i<nums.size();i++)
     {
        if(nums[i]=='0')
           if(nums[i-1]!='1'&&nums[i-1]!='2')
              return 0;
     }
     //dp[i]表示以i为结尾的翻译结果种数
     vector<int> dp(nums.size(),1);
     for(int i=1;i<nums.size();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; //数字是11~19或者21~26开头
            else dp[i]=dp[i-1]+dp[i-2]; //组合译码
        }
        else dp[i]=dp[i-1]; //直接译码
     }
     return dp[nums.size()-1];
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值