91:解码方法

This commit is contained in:
huangge1199 2021-04-21 16:43:02 +08:00
parent d52c652374
commit 7eb08912be
9 changed files with 485 additions and 0 deletions

View File

@ -0,0 +1,156 @@
//一条包含字母 A-Z 的消息通过以下映射进行了 编码
//
//
//'A' -> 1
//'B' -> 2
//...
//'Z' -> 26
//
//
// 解码 已编码的消息所有数字必须基于上述映射的方法反向映射回字母可能有多种方法例如"11106" 可以映射为
//
//
// "AAJF" 将消息分组为 (1 1 10 6)
// "KJF" 将消息分组为 (11 10 6)
//
//
// 注意消息不能分组为 (1 11 06) 因为 "06" 不能映射为 "F" 这是由于 "6" "06" 在映射中并不等价
//
// 给你一个只含数字的 非空 字符串 s 请计算并返回 解码 方法的 总数
//
// 题目数据保证答案肯定是一个 32 的整数
//
//
//
// 示例 1
//
//
//输入s = "12"
//输出2
//解释它可以解码为 "AB"1 2或者 "L"12
//
//
// 示例 2
//
//
//输入s = "226"
//输出3
//解释它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6)
//
//
// 示例 3
//
//
//输入s = "0"
//输出0
//解释没有字符映射到以 0 开头的数字
//含有 0 的有效映射是 'J' -> "10" 'T'-> "20"
//由于没有字符因此没有有效的方法对此进行解码因为所有数字都需要映射
//
//
// 示例 4
//
//
//输入s = "06"
//输出0
//解释"06" 不能映射到 "F" 因为字符串含有前导 0"6" "06" 在映射中并不等价
//
//
//
// 提示
//
//
// 1 <= s.length <= 100
// s 只包含数字并且可能包含前导零
//
// Related Topics 字符串 动态规划
// 👍 696 👎 0
package leetcode.editor.cn;
//91:解码方法
public class DecodeWays {
public static void main(String[] args) {
//测试代码
Solution solution = new DecodeWays().new Solution();
//2
System.out.println(solution.numDecodings("12"));
//3
System.out.println(solution.numDecodings("226"));
//0
System.out.println(solution.numDecodings("0"));
//0
System.out.println(solution.numDecodings("06"));
//1
System.out.println(solution.numDecodings("10"));
//1
System.out.println(solution.numDecodings("2101"));
//2
System.out.println(solution.numDecodings("2011"));
//3
System.out.println(solution.numDecodings("1201234"));
//0
System.out.println(solution.numDecodings("10011"));
}
//力扣代码
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int numDecodings(String s) {
// int length = s.length();
// if (is) {
// for (int i = 0; i < length - 1; i++) {
// if (s.charAt(i) == '0' && s.charAt(i + 1) == '0') {
// return 0;
// }
// }
// is = false;
// }
// if (s.startsWith("0")) {
// return 0;
// }
// if (length == 1) {
// return 1;
// }
// if (length == 2) {
// if ((s.charAt(0) - '0') * 10 + (s.charAt(1) - '0') <= 26 && s.charAt(1) != '0') {
// return 2;
// } else {
// return 1;
// }
// }
// if (s.charAt(length - 1) == '0') {
// return numDecodings(s.substring(0, length - 2));
// }
// if (s.charAt(length - 2) == '0') {
// return length > 3 ? numDecodings(s.substring(0, length - 3)) : 1;
// }
// if ((s.charAt(length - 2) - '0') * 10 + (s.charAt(length - 1) - '0') <= 26) {
// return numDecodings(s.substring(0, length - 2)) + numDecodings(s.substring(0, length - 1));
// } else {
// return numDecodings(s.substring(0, length - 1));
// }
if (s.startsWith("0")) {
return 0;
}
int length = s.length();
int[] nums = new int[length + 1];
nums[0] = 1;
for (int i = 1; i <= length; i++) {
if (i < length && s.charAt(i) == '0' && s.charAt(i - 1) == '0') {
return 0;
}
if (s.charAt(i - 1) != '0') {
nums[i] += nums[i - 1];
}
if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
nums[i] += nums[i - 2];
}
}
return nums[length];
}
}
//leetcode submit region end(Prohibit modification and deletion)
}

View File

@ -0,0 +1,66 @@
<p>一条包含字母 <code>A-Z</code> 的消息通过以下映射进行了 <strong>编码</strong> </p>
<pre>
'A' -> 1
'B' -> 2
...
'Z' -> 26
</pre>
<p><strong>解码</strong> 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,<code>"11106"</code> 可以映射为:</p>
<ul>
<li><code>"AAJF"</code> ,将消息分组为 <code>(1 1 10 6)</code></li>
<li><code>"KJF"</code> ,将消息分组为 <code>(11 10 6)</code></li>
</ul>
<p>注意,消息不能分组为  <code>(1 11 06)</code> ,因为 <code>"06"</code> 不能映射为 <code>"F"</code> ,这是由于 <code>"6"</code><code>"06"</code> 在映射中并不等价。</p>
<p>给你一个只含数字的 <strong>非空 </strong>字符串 <code>s</code> ,请计算并返回 <strong>解码</strong> 方法的 <strong>总数</strong></p>
<p>题目数据保证答案肯定是一个 <strong>32 位</strong> 的整数。</p>
<p> </p>
<p><strong>示例 1</strong></p>
<pre>
<strong>输入:</strong>s = "12"
<strong>输出:</strong>2
<strong>解释:</strong>它可以解码为 "AB"1 2或者 "L"12
</pre>
<p><strong>示例 2</strong></p>
<pre>
<strong>输入:</strong>s = "226"
<strong>输出:</strong>3
<strong>解释:</strong>它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
</pre>
<p><strong>示例 3</strong></p>
<pre>
<strong>输入:</strong>s = "0"
<strong>输出:</strong>0
<strong>解释:</strong>没有字符映射到以 0 开头的数字。
含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
由于没有字符,因此没有有效的方法对此进行解码,因为所有数字都需要映射。
</pre>
<p><strong>示例 4</strong></p>
<pre>
<strong>输入:</strong>s = "06"
<strong>输出:</strong>0
<strong>解释:</strong>"06" 不能映射到 "F" ,因为字符串含有前导 0<code>"6"</code><code>"06"</code> 在映射中并不等价)。</pre>
<p> </p>
<p><strong>提示:</strong></p>
<ul>
<li><code>1 <= s.length <= 100</code></li>
<li><code>s</code> 只包含数字,并且可能包含前导零。</li>
</ul>
<div><div>Related Topics</div><div><li>字符串</li><li>动态规划</li></div></div>\n<div><li>👍 696</li><li>👎 0</li></div>

View File

@ -0,0 +1,263 @@
#### 方法一:动态规划
**思路与算法**
对于给定的字符串 *s*,设它的长度为 *n*,其中的字符从左到右依次为 ![s\[1\],s\[2\],\cdots,s\[n\] ](./p__s_1_,_s_2_,_cdots,_s_n__.png) 。我们可以使用动态规划的方法计算出字符串 *s* 的解码方法数。
具体地,设 *f_i* 表示字符串 *s* 的前 *i* 个字符 *s[1..i]* 的解码方法数。在进行状态转移时,我们可以考虑最后一次解码使用了 *s* 中的哪些字符,那么会有下面的两种情况:
- 第一种情况是我们使用了一个字符,即 *s[i]* 进行解码,那么只要 ![s\[i\]\neq0 ](./p__s_i__neq_0_.png) ,它就可以被解码成 ![\text{A}\sim\text{I} ](./p__text{A}_sim_text{I}_.png) 中的某个字母。由于剩余的前 *i-1* 个字符的解码方法数为 *f_{i-1}*,因此我们可以写出状态转移方程:
![f_i=f_{i-1},\quad其中~s\[i\]\neq0 ](./p_______f_i_=_f_{i-1},_quad_其中_~_s_i__neq_0______.png)
- 第二种情况是我们使用了两个字符,即 *s[i-1]**s[i]* 进行编码。与第一种情况类似,*s[i-1]* 不能等于 *0*,并且 *s[i-1]**s[i]* 组成的整数必须小于等于 *26*,这样它们就可以被解码成 ![\text{J}\sim\text{Z} ](./p__text{J}_sim_text{Z}_.png) 中的某个字母。由于剩余的前 *i-2* 个字符的解码方法数为 *f_{i-2}*,因此我们可以写出状态转移方程:
![f_i=f_{i-2},\quad其中~s\[i-1\]\neq0~并且~10\cdots\[i-1\]+s\[i\]\leq26 ](./p_______f_i_=_f_{i-2},_quad_其中_~_s_i-1__neq_0_~并且~_10cdot_s_i-1_+s_i__leq_26______.png)
需要注意的是,只有当 *i>1* 时才能进行转移,否则 *s[i-1]* 不存在。
将上面的两种状态转移方程在对应的条件满足时进行累加,即可得到 *f_i* 的值。在动态规划完成后,最终的答案即为 *f_n*
**细节**
动态规划的边界条件为:
*
f_0 = 1
*
即**空字符串可以有 *1* 种解码方法,解码出一个空字符串**。
同时,由于在大部分语言中,字符串的下标是从 *0* 而不是 *1* 开始的,因此在代码的编写过程中,我们需要将所有字符串的下标减去 *1*,与使用的语言保持一致。
**代码**
```C++ [sol11-C++]
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
vector<int> f(n + 1);
f[0] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i - 1] != '0') {
f[i] += f[i - 1];
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
f[i] += f[i - 2];
}
}
return f[n];
}
};
```
```Java [sol11-Java]
class Solution {
public int numDecodings(String s) {
int n = s.length();
int[] f = new int[n + 1];
f[0] = 1;
for (int i = 1; i <= n; ++i) {
if (s.charAt(i - 1) != '0') {
f[i] += f[i - 1];
}
if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
f[i] += f[i - 2];
}
}
return f[n];
}
}
```
```Python [sol11-Python3]
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
f = [1] + [0] * n
for i in range(1, n + 1):
if s[i - 1] != '0':
f[i] += f[i - 1]
if i > 1 and s[i - 2] != '0' and int(s[i-2:i]) <= 26:
f[i] += f[i - 2]
return f[n]
```
```JavaScript [sol11-JavaScript]
var numDecodings = function(s) {
const n = s.length;
const f = new Array(n + 1).fill(0);
f[0] = 1;
for (let i = 1; i <= n; ++i) {
if (s[i - 1] !== '0') {
f[i] += f[i - 1];
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
f[i] += f[i - 2];
}
}
return f[n];
};
```
```go [sol11-Golang]
func numDecodings(s string) int {
n := len(s)
f := make([]int, n+1)
f[0] = 1
for i := 1; i <= n; i++ {
if s[i-1] != '0' {
f[i] += f[i-1]
}
if i > 1 && s[i-2] != '0' && ((s[i-2]-'0')*10+(s[i-1]-'0') <= 26) {
f[i] += f[i-2]
}
}
return f[n]
}
```
```C [sol11-C]
int numDecodings(char* s) {
int n = strlen(s);
int f[n + 1];
memset(f, 0, sizeof(f));
f[0] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i - 1] != '0') {
f[i] += f[i - 1];
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
f[i] += f[i - 2];
}
}
return f[n];
}
```
注意到在状态转移方程中,*f_i* 的值仅与 *f_{i-1}**f_{i-2}* 有关,因此我们可以使用三个变量进行状态转移,省去数组的空间。
```C++ [sol12-C++]
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
// a = f[i-2], b = f[i-1], c = f[i]
int a = 0, b = 1, c;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s[i - 1] != '0') {
c += b;
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
c += a;
}
tie(a, b) = {b, c};
}
return c;
}
};
```
```Java [sol12-Java]
class Solution {
public int numDecodings(String s) {
int n = s.length();
// a = f[i-2], b = f[i-1], c=f[i]
int a = 0, b = 1, c = 0;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s.charAt(i - 1) != '0') {
c += b;
}
if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
c += a;
}
a = b;
b = c;
}
return c;
}
}
```
```Python [sol12-Python3]
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
# a = f[i-2], b = f[i-1], c = f[i]
a, b, c = 0, 1, 0
for i in range(1, n + 1):
c = 0
if s[i - 1] != '0':
c += b
if i > 1 and s[i - 2] != '0' and int(s[i-2:i]) <= 26:
c += a
a, b = b, c
return c
```
```JavaScript [sol12-JavaScript]
var numDecodings = function(s) {
const n = s.length;
// a = f[i-2], b = f[i-1], c = f[i]
let a = 0, b = 1, c = 0;
for (let i = 1; i <= n; ++i) {
c = 0;
if (s[i - 1] !== '0') {
c += b;
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
c += a;
}
a = b;
b = c;
}
return c;
};
```
```go [sol12-Golang]
func numDecodings(s string) int {
n := len(s)
// a = f[i-2], b = f[i-1], c = f[i]
a, b, c := 0, 1, 0
for i := 1; i <= n; i++ {
c = 0
if s[i-1] != '0' {
c += b
}
if i > 1 && s[i-2] != '0' && ((s[i-2]-'0')*10+(s[i-1]-'0') <= 26) {
c += a
}
a, b = b, c
}
return c
}
```
```C [sol12-C]
int numDecodings(char* s) {
int n = strlen(s);
// a = f[i-2], b = f[i-1], c = f[i]
int a = 0, b = 1, c;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s[i - 1] != '0') {
c += b;
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
c += a;
}
a = b, b = c;
}
return c;
}
```
**复杂度分析**
- 时间复杂度:*O(n)*,其中 *n* 是字符串 *s* 的长度。
- 空间复杂度:*O(n)* 或 *O(1)*。如果使用数组进行状态转移,空间复杂度为 *O(n)*;如果仅使用三个变量,空间复杂度为 *O(1)*

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B