0%

leetCode-91:Decode Ways

问题描述

用数字126代表大写字母AZ,输入一个非空的数字字符串,要求输出这个数字字符串转成大写字母时有多少种转换的方式。题目链接:**点我**

样例输入输出

输入:”12”

输出:2

输入:”226”

输出:3

输入:”302”

输出:0

问题解法

此题可以转换为已知一个字符串的的转换方式数,求当加入一个新的字符组成一个新的字符串时的转换方式数。这很明显可以使用递归或动态规划解法。此处用动态规划进行求解。假设用 f(n) 表示前 n 个数的转换方式数,则可以得出以下的动态规划方程

  • 如果第 n 个数是0
    • 如果第 n - 1个数是1或者2,则 f(n) = f(n - 2),因为只能把最后两个数一起转换
    • 否则,f(n) = 0,此时没有任何转换方式满足
  • 如果第 n 个数不是 0,第 n - 1个数是 0,则 f(n) = f(n - 1),因为最后一个数必须单独进行转换
  • 如果第 n 个数和第 n - 1个数都不是0
    • 如果第 n - 1个数和第n - 2个数构成的数字小于等于26,则 f(n) = f(n - 1) + f(n - 2),因为此时可以将最后两个数一起转换,也可以将最后一个数单独转换,如果是最后两个数一起转换,则转换方式有 f(n - 2),如果是将最后一个数单独转换,则转换方式有 f(n - 1)
    • 如果第 n - 1个数和第n - 2个数构成的数字大于26,则 f(n) = f(n - 1),因为此时只能将最后一个数单独转换

初始化时,f(0) = 1, f(1) = 0(当前位是0,前一位大于2),或 f(1) = 1(当前位是0,前一位是1或2,或当前位不是0且与前一位构成数字大于26),或 f(1) = 2(当前位不是0,且与前一位构成的数字小于等于26)

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Solution 
{
public int numDecodings(String s)
{
int size = s.length();
if (size == 0 || s.startsWith("0"))
{
return 0;
}

if (size == 1)
{
return 1;
}

// a[i] 表示截止到i位置的拆分数
int[] a = new int[size];
a[0] = 1;
a[1] = 1;
if (s.charAt(1) == '0' && s.charAt(0) != '1' && s.charAt(0) != '2')
{
a[1] = 0;
}
else if (Integer.parseInt(s.substring(0, 2)) <= 26 && s.charAt(1) != '0')
{
a[1] = 2;
}

for (int i = 2; i < size; i++)
{
// 当前位是0,需要检查是否可以和前一位构成10或者20
// 如果可以,则拆分的数等于截止到前两个数的拆分数
// 否则,拆分数为0
if (s.charAt(i) == '0')
{
if (s.charAt(i - 1) == '1' || s.charAt(i - 1) == '2')
{
a[i] = a[i - 2];
}
else
{
a[i] = 0;
}
}
// 如果当前位不等于0,则判断前一位是否是0
// 如果是0,则拆分数等于截止前一位的拆分数
else if (s.charAt(i - 1) == '0')
{
a[i] = a[i - 1];
}
// 如果当前位和前一位都不等于0,需要判断是否和前一位构成的数小于等于26
// 如果小于等于26,则拆分数为截止到前一个数的拆分数加上截止到前两个数的拆分数
// 否则,拆分数为截止到前一个数的拆分数
else if (Integer.parseInt(s.substring(i - 1, i + 1)) <= 26)
{
a[i] = a[i - 1] + a[i - 2];
}
else
{
a[i] = a[i - 1];
}
}

return a[size - 1];
}
}