91. 解码方法
可以访问我的博客,吼吼
题目表述
一条包含字母 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) 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-ways
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
还是利用动态规划。但与上一题最长回文子串不同的是,这道题的状态转移方程不一样,但都差不多。观察状态转移方程发现,可以将二维的数组压缩到一维。最后总结一下常见的状态转移方程的形式。
ps.弱弱地说一句,其实上一题也是可以优化,由
m
×
n
m\times n
m×n维变成
2
×
n
2\times n
2×n维的。
下面开始吧
用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示字符串 s [ i : j ] s[i:j] s[i:j]编码的最大方案数。
状态转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
]
[
j
−
k
−
1
]
∣
1
≤
s
[
j
−
k
:
j
]
≤
26
,
0
≤
k
≤
m
i
n
{
2
,
j
−
i
+
1
}
}
dp[i][j]=max\{dp[i][j-k-1]\ |\ 1\leq s[j-k:j]\leq 26,0\leq k\leq min\{2,j-i+1\} \}
dp[i][j]=max{dp[i][j−k−1] ∣ 1≤s[j−k:j]≤26,0≤k≤min{2,j−i+1}}
我试着说明一下。
假设
d
p
[
i
]
[
j
−
1
]
dp[i][j-1]
dp[i][j−1]已经知道了,也就是说字符串
s
[
i
:
j
]
s[i:j]
s[i:j]的编码方案数已知,现在考虑地第
j
j
j位字符。需要做的是,选择从
j
j
j位往前,选择
k
k
k位字符,将它们跟第
j
j
j位拼起来并看成一个整体,也就是将
s
[
i
:
j
]
s[i:j]
s[i:j]分成
s
[
i
:
j
−
k
−
1
]
s[i:j-k-1]
s[i:j−k−1]和
s
[
j
−
k
:
j
]
s[j-k:j]
s[j−k:j]两个部分,注意,这样分的前提是
s
[
j
−
k
:
j
]
s[j-k:j]
s[j−k:j]是可编码的,也就是对应着
1
≤
s
[
j
−
k
:
j
]
≤
26
1\leq s[j-k:j]\leq 26
1≤s[j−k:j]≤26这个条件。举个例子,
s
=
226
s=226
s=226,现在考虑
6
6
6这一位,如下图:
这是两种不同的方案,然后去这两中方案中的最大那一种就可以,也就是对应着公式中
m
a
x
{
d
p
[
i
]
[
j
−
k
−
1
]
}
max\{dp[i][j-k-1]\}
max{dp[i][j−k−1]}的部分。
因为从1到26,最大就是2位数,所以
k
k
k最大是1,也就是最多往前选1个字符,又因为还需要保证
s
[
i
:
j
−
k
−
1
]
s[i:j-k-1]
s[i:j−k−1]不为空,所以需要
i
≤
j
−
k
−
1
i\leq j-k-1
i≤j−k−1,即
k
≤
j
−
i
+
1
k\leq j-i+1
k≤j−i+1,这就对应了公式中
0
≤
k
≤
m
i
n
{
2
,
j
−
i
+
1
}
0\leq k\leq min\{2,j-i+1\}
0≤k≤min{2,j−i+1}的部分。
然后说一下空间优化的问题。
从上面的状态转移方程可以看出,求
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]只需要第
i
i
i行
j
j
j左边的元素就行,用不着其他行的元素,所有可以二维降成一维。
降为一维后,还是进行跟原来类似的操作,只不过就是对这一维数组进行了多次操作,如下图。
优化之后的状态转移方程就变成了:
d
p
[
j
]
=
m
a
x
{
d
p
[
j
−
k
−
1
]
∣
1
≤
s
[
j
−
k
:
j
]
≤
26
,
0
≤
k
≤
m
i
n
{
2
,
j
−
i
+
1
}
}
dp[j]=max\{dp[j-k-1]\ |\ 1\leq s[j-k:j]\leq 26,0\leq k\leq min\{2,j-i+1\} \}\\
dp[j]=max{dp[j−k−1] ∣ 1≤s[j−k:j]≤26,0≤k≤min{2,j−i+1}}
其中
i
i
i表示当前是第几次操作。
下面总结一下常见的状态转移形式,方程就不写了,记不住,就把图画一画吧。
紫色五角星表示要求的目标,蓝色表示求这个目标需要那些元素
第一类:
第二类:
第三类:
代码实现
class Solution {
public:
int numDecodings(string s) {
if (s.length() <= 0) return 0;
set<string> se {"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"};
int dp[3050] = {0};
for (int i = 0; i < s.length(); i++) {
if (s[i] != '0') {
dp[i] = 1;
}
}
int m = s.length();
for (int t = 1; t < m; t++) {
int i, j;
i = m - t - 1;
for (j = i+1; j < m; j++) {
int cur = 0;
for (int k = 0; k <= min(1, j-i+1); k++) {
if (se.find(s.substr(j-k, k+1)) != se.end()) {
if (j - k - 1 < i) {
cur += 1;
}
else
cur += dp[j-k-1];
}
}
dp[j] = cur;
}
}
return dp[m-1];
}
};