原题链接:https://leetcode.com/problems/decode-ways/
1. 题目介绍
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.
有一种编码方式是用数组代表字母。1代表A,2代表B······26代表Z。
给出一个非空字符串,字符串只由数字组成,由多少种方法去破译它。
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).
2. 解题思路
2.1 动态规划法
可以考虑用动态规划法来解决此问题。
对于字符串的每一个数字来说,有两种破译的方式
- 当前数字本身代表一个字母,比如1就代表A,2就代表B,6就代表F。这种破译方式要求数字不等于0。
- 当前数字和前一个数字共同代表一个字母,当前数字只是个位。比如字符串是26,这个6是当前数字,它可以和前面的2共同代表字母Z。这种破译方式要求前一个数字是1,或者是2;并且当前一个数字是2时,当前数字必须小于等于6。
因此我们可以构造一个一维数组dp[],dp的长度和字符串的字符数相同。dp[ i ]代表截止第 i 个字符,破译的方式有多少种。
若第 i 个数字采用上面第 1 种破译方式,相当于在已经破译得到的字符串的最后再加上一个字母,因此dp[ i ] = dp[ i -1 ]
若第 i 个数字采用上面第 2 种破译方式,相当于在前面第i-2个字符所得到的字符串的最后再加上一个字母,因此dp[ i ] = dp[ i -2 ]
对于每一个数字,都要考虑两种破译方式,这两种破译方式得到的结果不冲突,都要算在内。
实现代码
class Solution {
public int numDecodings(String s) {
int length = s.length();
if(length ==0) {
return 0;
}
if(s.charAt(0) == '0') {
return 0;
//如果字符串的第一个字符是“0”,那么无法破译,返回0
}
if(length ==1) {
return 1;
}
int [] dp = new int [length];
dp[0] = 1;
//只考虑个位的情况
if(s.charAt(1) != '0') {
dp[1] += dp[0];
}
//十位为1,个位随意的情况
if(s.charAt(0) == '1' ) {
dp[1] += 1;
}
//十位为2,个位小于等于6的情况
if(s.charAt(0) =='2' && s.charAt(1) <='6' ) {
dp[1] += 1;
}
for(int i = 2;i<length;i++) {
char now = s.charAt(i);
char before = s.charAt(i-1);
//只考虑个位的情况
if(now != '0') {
dp[i] += dp[i-1];
}
//十位为1,个位随意的情况
if(before == '1' ) {
dp[i] += dp[i-2];
}
//十位为2,个位小于等于6的情况
if(before == '2' && now <='6' ) {
dp[i] += dp[i-2];
}
}
return dp[length-1];
}
}
2.2 进一步改进:抛弃一维数组
可以看到每次更新dp[ i ] 只用到了dp[ i-2 ]和dp[ i-1 ],没有用到dp数组的其他值,因此可以使用三个整数来代替dp数组,节省空间。
实现代码
class Solution {
public int numDecodings(String s) {
int length = s.length();
if(length ==0) {
return 0;
}
if(s.charAt(0) == '0') {
return 0;
//如果字符串的第一个字符是“0”,那么无法破译,返回0
}
if(length ==1) {
return 1;
}
int a,b,c;
a = 1;
b = 0;
c = 0;
//只考虑个位的情况
if(s.charAt(1) != '0') {
b += a;
}
//十位为1,个位随意的情况
if(s.charAt(0) == '1' ) {
b += 1;
}
//十位为2,个位小于等于6的情况
if(s.charAt(0) =='2' && s.charAt(1) <='6' ) {
b += 1;
}
for(int i = 2;i<length;i++) {
char now = s.charAt(i);
char before = s.charAt(i-1);
//只考虑个位的情况
if(now != '0') {
c += b;
}
//十位为1,个位随意的情况
if(before == '1' ) {
c += a;
}
//十位为2,个位小于等于6的情况
if(before == '2' && now <='6' ) {
c += a;
}
a = b;
b = c;
c = 0;
}
return b;
}
}