JavaScript|LeetCode|动态规划|91.解码方法

法1:动态规划
想法:

  1. 用数组dp保存以当前数字字符结尾的串的解码方法总数
  2. 对于当前数字字符s[i],若s[i - 1]s[i]衔接而成的数字可解码(即 >=1 且 <= 26),则dp[i] = dp[i - 2] + dp[i - 1](dp[i - 2]表示s[i - 1]s[i]一起解码,再加上s[i - 1]之前的字符的解码数;dp[i - 1]表示s[i]单独解码,再加上s[i]之前的字符的解码数)
  3. 对于当前数字字符s[i],若s[i - 1]s[i]衔接而成的数字不可解码,则s[i]单独解码:dp[i] = dp[i - 1]
  4. 需特殊考虑s[i] == '0’的问题 ('0’只可与它的前一位衔接解码,否则无法解码)
  • 若s[0] == ‘0’,则无法解码,return 0
  • 对于s[i],若s[i] == ‘0’,需判断s[i - 1]s[i]是否可解码:可解码则dp[i] = dp[i - 2];不可解码,则return 0
  • 对于s[i],若s[i] != ‘0’,则需判断s[i - 1]是否为’0’:若为’0’则s[i]只能单独解码,dp[i] = dp[i - 1];若不为’0’则可考虑s[i - 1]s[i]组合解码(如2、3中所述)
/** 
* @param {string} s 
* @return {number} 
*/
var numDecodings = function(s) {    
    if(s[0] == '0') { // 0在首位,则不可能解码成功        
        return 0;    
    }    
    if(s.length == 1) { // 只有一位,且第一位不是0        
        return 1;    
    }    
    var dp = [];    
    var i = 0;
    // 先对dp[0]和dp[1]初始化    
    dp[0] = 1;    
    if(s[1] == '0') { // s[1]不可单独解码        
        if(s[0] == '1' || s[0] == '2') { // 只有'10' '20'可解码            
            dp[1] = 1;        
        }        
        else {            
            return 0;        
        }    
    }    
    else {        
        if(Number(s[0] + s[1]) >= 1 && Number(s[0] + s[1]) <= 26) {            
            // s[0]s[1]可解码            
            dp[1] = 2; // 1. s[0]和s[1]分开解码;2. s[0]s[1]一起解码        
        }        
        else {            
            dp[1] = 1; // s[0]和s[1]分开解码        
        }    
    }
    
    for(i = 2; i < s.length; i++) {        
        if(s[i] == '0') { // 出现0,则0必须和s[i - 1]能够一起解码            
            if(s[i - 1] == '1' || s[i - 1] == '2') {                
                dp[i] = dp[i - 2];            
            }            
            else {                
                return 0;            
            }        
        }        
        else { // 不为0            
            if(s[i - 1] == '0') { // 则s[i]只能单独解码                
                dp[i] = dp[i - 1];            
            }            
            else {  // s[i]可能可以与s[i - 1]解码                
                if(Number(s[i - 1] + s[i]) >= 1 && Number(s[i - 1] + s[i]) <= 26) {                    
                    // s[i - 1]s[i]可解码                    
                    dp[i] = dp[i - 1] + dp[i - 2];                
                }                
                else {                    
                    dp[i] = dp[i - 1];                
                }            
            }        
        }    
    }    
    return dp[s.length - 1];
};

优化1
想法:

  1. dp[i]只与dp[i - 1]、dp[i - 2]有关,故:pre1保存dp[i - 1],pre2保存dp[i - 2],cur保存dp[i]
/** 
* @param {string} s 
* @return {number} 
*/
var numDecodings = function(s) {    
    if(s[0] == '0') { // 0在首位,则不可能解码成功        
        return 0;    
    }    
    if(s.length == 1) { // 只有一位,且第一位不是0        
        return 1;    
    }    
    var pre1 = 0, pre2 = 0, cur = 0;    
    var i = 0;
    // 先对dp[0]和dp[1]初始化    
    pre2 = 1;    
    if(s[1] == '0') { // s[1]不可单独解码        
        if(s[0] == '1' || s[0] == '2') { // 只有'10' '20'可解码            
            pre1 = 1;        
        }        
        else {            
            return 0;        
        }    
    }    
    else {        
        if(Number(s[0] + s[1]) >= 1 && Number(s[0] + s[1]) <= 26) {            
            // s[0]s[1]可解码            
            pre1 = 2; // 1. s[0]和s[1]分开解码;2. s[0]s[1]一起解码        
        }        
        else {            
            pre1 = 1; // s[0]和s[1]分开解码        
        }    
    }
    
    for(i = 2; i < s.length; i++) {        
        if(s[i] == '0') { // 出现0,则0必须和s[i - 1]能够一起解码            
            if(s[i - 1] == '1' || s[i - 1] == '2') {                
                cur = pre2;            
            }            
            else {                
                return 0;            
            }        
        }        
        else { // 不为0            
            if(s[i - 1] == '0') { // 则s[i]只能单独解码                
                cur = pre1;            
            }            
            else {  // s[i]可能可以与s[i - 1]解码                
                if(Number(s[i - 1] + s[i]) >= 1 && Number(s[i - 1] + s[i]) <= 26) {                    // s[i - 1]s[i]可解码                    
                    cur = pre1 + pre2;                
                }                
                else {                    
                    cur = pre1;                
                }            
            }        
        }        
        pre2 = pre1;        
        pre1 = cur;    
    }    
    return pre1;
};

优化2
看了题解,换了一种分类方式
想法:

  1. 若s[i] == ‘0’,则只有当s[i - 1] 为’1’或’2’时,s[i - 1]s[i]可共同解码:cur = pre2
  2. 若s[i] != ‘0’,则:
  • 若s[i - 1] == ‘1’,则s[i - 1]和s[i]可分开解码,也可一起解码:cur = pre1 + pre2
  • 若s[i - 1] == ‘2’ && s[i] >= ‘1’ && s[i] <= ‘6’,则s[i - 1]和s[i]可分开解码,也可一起解码:cur = pre1 + pre2
  • 以上两种情况都不满足,则只有s[i]单独解码:cur = pre1
    注:pre1为以s[i - 1]字符结尾的串的解码方法总数;pre2为以s[i - 2]字符结尾的串的解码方法总数;cur为以s[i]字符结尾的串的解码方法总数
/** 
* @param {string} s 
* @return {number} 
*/
var numDecodings = function(s) {    
    if(s[0] == '0') { // 0在首位,则不可能解码成功        
        return 0;    
    }    
    var pre1 = 0, pre2 = 0, cur = 0;    
    var i = 0;    
    pre1 = pre2 = 1;    
    for(i = 1; i < s.length; i++) { // 从第二位数字开始        
        if(s[i] == '0') { // 出现0,则0必须和s[i - 1]能够一起解码            
            if(s[i - 1] == '1' || s[i - 1] == '2') {                
                cur = pre2;            
            }            
            else {                
                return 0;            
            }        
        }        
        else { // 不为0            
            if(s[i - 1] == '1') {                 
                cur = pre1 + pre2;            
            }            
            else if(s[i - 1] == '2' && (s[i] >= '1' && s[i] <= '6')){                 
                cur = pre1 + pre2;            
            }            
            else {                
                cur = pre1;            
            }        
        }        
        pre2 = pre1;        
        pre1 = cur;    
    }    
    return pre1;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值