一条包含字母A - Z的消息通过以下映射进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
输入:s = “12”
输出:2
解释:它可以解码为 “AB”(1 2)或者 “L”(12)。
输入:s = “226”
输出:3
解释:它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
思路:动态规划
用vector< int > dp数组来表示前i个数字解码的方式数。
记当前第 i 位的数字为 ai,第i-1位数字为 ai-1
则有如下转移方程:
class Solution {
public:
bool check(string s) {
if (s[0] == '0') return false;
int n = stoi(s);
return (n <= 26 && n >= 1);
}
int numDecodings(string s) {
int n = s.length();
int i;
if (s[0] == '0') return 0;
vector<int> dp(n+1, 1);
for(i = 2; i <= n; i++)
{
bool flag1 = check(s.substr(i-2, 2));
bool flag2 = check(s.substr(i-1, 1));
if (flag1 && flag2) dp[i] = dp[i-2] + dp[i-1];
else if (flag1) dp[i] = dp[i-2];
else if (flag2) dp[i] = dp[i-1];
else return 0;
}
return dp[n];
}
};
可以简化,令空间复杂度为O(1)
- dp[i]为str[0…i]的编码方法总数
- 分情况:
- 若s[i] = ‘0’,那么若s[i - 1] = ‘1’ or ‘2’,则dp[i] = dp[i - 2];否则return 0
- 解释:由于0无法单独映射字母,必须跟s[i - 1]组合,s[i - 1] + s[i]唯一,则dp[i] = dp[i - 2] 不增加情况。
- 若s[i - 1] = ‘1’,则dp[i] = dp[i - 1] + dp[i - 2]
- 解释,如果s[i - 1]与s[i]分开编码,则为dp[i - 1];如果合并编码,同上述情况,则为dp[i - 2]
- 若s[i - 1] = ‘2’,并且 ‘1’ <= s[i] <= ‘6’,则dp[i] = dp[i - 1] + dp[i - 2]
- 解释,由于s[i - 1] = ‘2’,并且’1’ <= s[i] <= ‘6’,则也是可以分两种情况进行编码。
- 除此之外的其他情况,
- 若s[i] = ‘0’,那么若s[i - 1] = ‘1’ or ‘2’,则dp[i] = dp[i - 2];否则return 0
- 由上述分析可知,dp[i]仅可能与前两项有关,因此可以用单变量代替dp[]数组,将空间复杂度从O(n)降到O(1)
int numDecodings(string s) {
if (s[0] == '0') return 0;
int pre = 1, curr = 1;//dp[-1] = dp[0] = 1
for (int i = 1; i < s.size(); i++) {
int tmp = curr;
if (s[i] == '0')
if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre;
else return 0;
else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6'))
curr = curr + pre;
pre = tmp;
}
return curr;
}