Leetcode_Dynamic_Programming -- 91. Decode Ways [medium]

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given a non-empty string containing only digits, determine the total number of ways to decode it.

26个英文字母分别代表一种映射关系,现给定一个非空字符串,用26个字母的映射关系对字符串进行解码,分析总共有多少种解码方式。

Example 1:

Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).

Example 2:

Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

这道题的分析的情况比较多,稍微有点复杂,利用动态规划来做:

创建dp数组,其长度与s的长度相同,dp[ i ] 代表以s[ i ]为结尾的字符串有多少种组成字符串的方法。初始化时,将dp全部初始化为0,代表s的所有位置都不能被解码。

一、分析边界情况,如果s是以‘0’开头,那么必定无法被解码,返回0;

二、如果s的长度为一,则无论如何都只有一种解码方式,直接返回1;

三、

A、s长度大于等于2的情况,首先初始化dp[ 0 ] = 1 ,由于在后续分析中会涉及到 i-1和 i-2 的情况,所以初始化时还需要考虑dp[ 1 ]的情况,我们来根据s[ i ]的值对dp[ 1 ]进行分析:s[ i ]在任何情况下只有 0~9 十种情况。首先如果 s[ 1 ] = '0' 时,‘0’必须和前一位进行组合才能得到一种解码方式,且其前一个数必须是‘1’或‘2’,否则无法解码;当 1<= int( s[ 1 ] ) < = 6时,s[ 1 ]既可以作为单独的一位字符与前面的字符串进行组合,这个时候dp[ 1 ] = 2,同时也可以与前一位的字符串进行组合,组合的时候必须保证前一位字符为‘1’或‘2’,这个时候dp[ 1 ] =1;剩下一情况就是  7 <= int(s[ 1 ]) <= 9 ,s[ 1 ]既可以作为单独的一位字符与前面字符串进行组合,这个时候dp[ 1 ] = 2,同时也可以与前一位字符串进行组合,组合的时候必须保证前一位字符为‘1’,这个时候dp[ 1 ] =1,以上就将dp[ 1 ]的情况分析清楚,直接初始化就行了;

B、下面我们考虑s字符串长度大于2的情况,由于s字符串索引从0开始,前面dp[ 0 ]和dp[ 1 ]已经分析完毕,所以for循环应该从2开始,代表从s的第三个字符开始。此时的分析与dp[ 1 ]的分析几乎相同,s[ i ]在任何情况下仍然只有 0~9 十种情况,当s[ i ] = ‘0’时,‘0’必须和前一位进行组合才能得到一种解码方式,且其前一个数必须是‘1’或‘2’,这个时候dp[ i ] = dp[ i-2 ],否则无法解码,返回0;当 1<= int( s[ 1 ] ) < = 6时,s[ i ]既可以作为单独的一位字符与前面的字符串进行组合,这时dp[ i ] = dp[ i-1 ],同时也可以与前一位的字符串进行组合,组合的时候必须保证前一位字符为‘1’或‘2’,这种情况下dp[ i ] = dp[ i-1 ] + dp[ i-2 ];当7 <= int(s[ 1 ]) <= 9 时,s[ i ]既可以作为单独的一位字符与前面字符串进行组合,即dp[ i ] =dp[ i-1 ],同时也可以与前一位字符串进行组合,组合的时候必须保证前一位字符为‘1’,这时dp[ i ] = dp[ i-1 ]+dp[ i-2 ] 。综上情况分析完毕,直接根据上述条件写出代码,返回dp最后一个元素即可。

总体方法可能稍显麻烦,但是理解上思路相对清晰些,本题思路与Reference的思路差不多,都是比较朴素的方法,如果解释没有看懂可以移步Reference

 

Solutions:

Python

class Solution:
    def numDecodings(self, s: str) -> int:
        if s[0]=='0':
            return 0
        if len(s) == 1:
            return 1
        dp = [0 for i in range(len(s))]
        dp[0] = 1
        
        if s[1] == '0':
            if s[0] == '1' or s[0] =='2':
                dp[1] = 1
        elif 1<= int(s[1]) <= 6:
            if s[0] == '1' or s[0] =='2':
                dp[1] =2
            else:
                dp[1] = 1
        else:
            if s[0] == '1':
                dp[1] = 2
            else:
                dp[1] = 1
            
        for i in range(2,len(s)):
            if s[i] == '0':
                if s[i-1] =='1' or s[i-1]=='2':
                    dp[i] = dp[i-2]
                else:
                    return 0
            elif (1 <= int(s[i])) and (int(s[i]) <= 6):
                if (s[i-1] == '1' or s[i-1] == '2'):
                    dp[i] = dp[i-1]+dp[i-2]
                else:
                    dp[i] = dp[i-1]
            else:
                if s[i-1] == '1':
                    dp[i] = dp[i-2] +dp[i-1]
                else:
                    dp[i] = dp[i-1]

        return dp[-1]
                    
                
        
    

Reference:

https://www.cnblogs.com/tengdai/p/9245714.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值