[dp] LeetCode 91. Decode Ways

输入:一个字符串,只包含0-9的字符。
输出:解码种类
规则:有一种信息映射规则 A->1,B->2…Z->26。
例如输入’1’,只能解码为A。
输入’12’,可以解码为’AB’,也可以是’L’,有2种解码方式。
分析:这一个分析思路很自然,但是很麻烦,最后还是有逻辑漏洞(有漏洞是因为没有把情况考虑全了)。
向这种多阶段完成的题目已经做过很多,不假思索就是这样想的。
如果需要解码’12128’这个字符串,
如果只有1,一种解法:A
如果是12,可以看做1+2,也可以看做12。解码就是A+B,或者L。
如果是121,可以看做是12+1,也可以看做是1+21。
 如果是12+1,解法种类等于12的解法:2。
 如果是1+21,解法种类等于1的解法:1。
 最终结果是3。
如果是1212,可以看做是121+2,也可以看做是12+12。
 如果是121+2,解法种类等于121的解法:3。
 如果是12+12,解法种类等于12的解法:2。
 最终结果是5。
如果是12127,可以看做是1212+7,也可以看做是121+27?错了,27没有对应的字母,所以只有1212+7这一种。
 如果是1212+7,解法种类等于1212的解法:5。
看到这里其实动态转移方程已经出来了。

当s[i] !=0 的时候:
dp[i]=dp[i-1]+dp[i-2],这是当Number(i-1,i)<=26的时候。
dp[i]=dp[i-1] 这是当Number(i-1,i)>26的时候。

对0字符还没有考虑,0没有对应的字母,那结果是怎样的?
对于’012’,因为0没有对应直接返回0。也就是0在首位,直接返回0。
对于’10’,可以分解为10+2两个部分,解码为’JB’,答案为1。
对于’210’,可以分解为2+10两个部分,分解数量和’2’是一样的,也就是dp[i] = dp[i-2]。
对于’280’,没有分解策略,应该返回0。
对于’100’,分解为10+0不对,0没有映射,分解为1+00,同样不对,0还是没有映射。应该直接返回0。

当s[i] ==0的时候:
if Number(i-1,1)<=26,那么dp[i] = dp[i-2]if Number(i-1,1)>26,则返回0。
if 两个连续的0挨着,则返回0。
if 0在第0个位置,则无效,返回0。

这道题目和爬楼梯是一个类型,只是有更多的限制条件。需要将各种情况思考一遍。参考链接

	public int numDecodings(String s) {
        if(s==null || s.length()==0 ) return 0;
        int  n = s.length();
        int[] dp = new int[n];
        if(s.charAt(0)=='0') return 0;
        dp[0] = 1;
        if(n>1){
            if(s.charAt(1)=='0'){
                if(s.charAt(0)=='0'){
                    return 0;
                }else if((s.charAt(0) - '0')*10 + (s.charAt(1) - '0')<=26){
                    dp[1] = dp[0];
                }else{
                    return 0;
                }
            }else{
                dp[1] = (s.charAt(0) - '0')*10 + (s.charAt(1) - '0')<=26?dp[0]+1:1;
            }            
        }
        for(int i =2;i<n;i++){
            if(s.charAt(i)=='0'){
                if(s.charAt(i-1)!='0' && (s.charAt(i-1) - '0')*10 + (s.charAt(i) - '0')<=26){
                    dp[i] = dp[i-2];
                }else {
                    return 0;
                }
                
            }else{
                if(s.charAt(i-1)=='0'){
                    dp[i] = dp[i-1];
                }else if( (s.charAt(i-1) - '0')*10 + (s.charAt(i) - '0')<=26){
                    dp[i] = dp[i-1] +dp[i-2];
                }else{
                    dp[i] = dp[i-1] ;
                }
            }
        }
        return dp[n-1];
         
    }

分析2:从后往前看。参考链接

例子依然是字符串’12128’
8:解法总数(8)=1
28:2+8,解法总数(28)=解法总数(8)
128:1+count(‘28’),或者12+8,解法总数(128)=解法总数(28)+解法 总数(8)
2128: 2+128;21+count(28),解法总数(2128)=解法总数(128)+解法 总数(28)

字符串’012’
2
12: 12 或者 1+2
012:0和谁都不能分到一起解码,所以为0。

字符串 ‘100’
0:答案0
00 :答案 0
100:答案 0 因为前两步都是0

字符串’1002’

当字符中包含0,对于当前位置不等于0的来说,number(i,i+1)<=26的话,解法总数(i)=解法总数(i+1)+解法总数(i+2),和上面的规则是相符 的。这样来考虑就简单了许多。
这样从后向前考虑问题,以及对于0的处理,都是要学习的地方 。

	public int numDecodings(String s) {
        if(s==null || s.length()==0 ) return 0;
        int  n = s.length();
        int[] dp = new int[n+1];
        dp[n] = 1;
        dp[n-1] = s.charAt(n-1)=='0'?0:1;
        for(int i=n-2;i>=0;i--){
            if(s.charAt(i)=='0'){
                dp[i] = 0;
            }else if((s.charAt(i)-'0')*10 + (s.charAt(i+1)-'0')<=26){
                dp[i] = dp[i+1]+dp[i+2];
            }else{
                dp[i] = dp[i+1];
            }
            
        }
        return dp[0];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值