一条包含字母 A-Z 的消息通过以下方式进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: “12”
输出: 2
解释: 它可以解码为 “AB”(1 2)或者 “L”(12)。
示例 2:
输入: “226”
输出: 3
解释: 它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
这题用递归或者动态规划 ,自己有点想不出来,看了答案以后明白啦
递归
private int getAns(String s, int start) {
//划分到了最后返回 1
if (start == s.length()) {
return 1;
}
//开头是 0,0 不对应任何字母,直接返回 0
if (s.charAt(start) == '0') {
return 0;
}
//得到第一种的划分的解码方式
int ans1 = getAns(s, start + 1);
int ans2 = 0;
//判断前两个数字是不是小于等于 26 的
if (start < s.length() - 1) {
int ten = (s.charAt(start) - '0') * 10;
int one = s.charAt(start + 1) - '0';
if (ten + one <= 26) {
ans2 = getAns(s, start + 2);
}
}
return ans1 + ans2;
}
*/
改进的递归 用memo[] 记录
class Solution {
public int numDecodings(String s) {
if(s==null || s.length()==0) return 0;
int[] memo=new int[s.length()+1] ;
return helper( s, 0, memo );
}
public int helper(String s, int start, int[] memo ){
if(start==s.length()) return 1;
if(memo[start]!=0) return memo[start];
if(s.charAt(start)=='0'){
return 0;
}
int ans1 = helper(s, start+1, memo);
int ans2 = 0;
if(start<s.length()-1){
int ten=(s.charAt(start)-'0')*10;
int one=s.charAt(start+1)-'0';
if(ten+one<=26){
ans2= helper(s, start+2, memo);
}
}
memo[start]=ans1+ans2;
return ans1+ans2;
}
}
动态规划 dp[ ]
同样的,递归就是压栈压栈压栈,出栈出栈出栈的过程,我们可以利用动态规划的思想,省略压栈的过程,直接从 bottom 到 top。
用一个 dp 数组, dp [ i ] 代表字符串 s [ i, s.len-1 ],也就是 s 从 i 开始到结尾的字符串的解码方式。
这样和递归完全一样的递推式。
如果 s [ i ] 和 s [ i + 1 ] 组成的数字小于等于 26,那么
dp [ i ] = dp[ i + 1 ] + dp [ i + 2 ]
从尾到头for循环
public int numDecodings(String s) {
int len = s.length();
int[] dp = new int[len + 1];
dp[len] = 1; //将递归法的结束条件初始化为 1
//最后一个数字不等于 0 就初始化为 1
if (s.charAt(len - 1) != '0') {
dp[len - 1] = 1;
}
for (int i = len - 2; i >= 0; i--) {
//当前数字时 0 ,直接跳过,0 不代表任何字母
if (s.charAt(i) == '0') {
continue;
}
int ans1 = dp[i + 1];
//判断两个字母组成的数字是否小于等于 26
int ans2 = 0;
int ten = (s.charAt(i) - '0') * 10;
int one = s.charAt(i + 1) - '0';
if (ten + one <= 26) {
ans2 = dp[i + 2];
}
dp[i] = ans1 + ans2;
}
return dp[];
}
dp[] 空间优化
接下来就是,动态规划的空间优化了,例如5题,10题,53题,72题等等都是同样的思路。都是注意到一个特点,当更新到 dp[ i ] 的时候,我们只用到 dp [ i + 1] 和 dp [ i + 2],之后的数据就没有用了。所以我们不需要 dp 开 len + 1 的空间。
简单的做法,**我们只申请 3 个空间,**然后把 dp 的下标对 3 求余就够了。
注:所有的dp【】里都加上%3即可
class Solution {
public int numDecodings(String s) {
if(s.length()==0 || s==null) return 0;
int[] dp=new int[3];
dp[s.length()%3]=1;
if(s.charAt(s.length()-1)!='0')
dp[(s.length()-1)%3] =1;
for(int i=s.length()-2;i>=0;i--){
if(s.charAt(i)=='0'){
dp[i%3]=0;//这里很重要,因为空间复用了,不要忘记归零
continue;
}
int ans1=dp[(i+1)%3];
int ans2=0;
int ten=(s.charAt(i)-'0')*10;
int one=(s.charAt(i+1)-'0');
if( ten+one<=26 ){
ans2=dp[(i+2)%3];
}
dp[i%3]=ans1+ans2;
}
return dp[0];
}
}
/*
两个指针两个空间就行 cur end
然后,如果多考虑以下,我们其实并不需要 3 个空间,我们只需要 2 个就够了,只需要更新的时候,指针移动一下,代码如下。
public int numDecodings5(String s) {
int len = s.length();
int end = 1;
int cur = 0;
if (s.charAt(len - 1) != '0') {
cur = 1;
}
for (int i = len - 2; i >= 0; i--) {
if (s.charAt(i) == '0') {
end = cur;//end 前移
cur = 0;
continue;
}
int ans1 = cur;
int ans2 = 0;
int ten = (s.charAt(i) - '0') * 10;
int one = s.charAt(i + 1) - '0';
if (ten + one <= 26) {
ans2 = end;
}
end = cur; //end 前移
cur = ans1 + ans2;
}
return cur;
}