首页 > 代码库 > [LeetCode] [动态规划] Decode Ways

[LeetCode] [动态规划] Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

‘A‘ -> 1
‘B‘ -> 2
...
‘Z‘ -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2.

问题描述:一条消息由A-Z的字母组成,将该消息按照上面的对应关系进行编码。给定一条已经经过编码的数字消息,求有多少种方式来对它进行解码。

由于一个字母最多只能编码成两位,因此,判断的时候最多只需要判断两位即可。

于是,有下面的解法:

设num[i]表示s[0]~s[i]的解码数,那么

numDecodings(s[0]~s[i]) = numDecodings(s[0]~s[i-2]) * canDecoded(s[i-1]~s[i]) + numDecodings(s[0]~s[i-1]) * canDecoded(s[i])

numDecodings()表示参数给定的字符串的解码数

canDecoded()表示参数给定的字符串是否可以解码,参数可以是两个字符组成的字符串,可以是一个字符组成的字符串,其实也可以用numDecodings()函数,只是为了区分它们所表示的意义而用不同的函数,而且canDecoded()的返回值只能是0或者1。

用这样实现的代码提交后结果是TLE,因此,需要对算法进行优化。

从中可以挖掘出动态规划的两个特性:

(1)最优子结构:就是上述方程式;

(2)重叠子结构:如上述方程式中,在求numDecodings(s[0]~s[i-1])时,又要计算numDecodings(s[0]~s[i-2])。

因此,可以使用动态规划的思想来进行优化。动态规划一个主要的特性是通过保存中间结果的方式来消除递归带来的时间复杂度。

假设num[]保存了字符串的解码方式数。

假设已知s[0]~s[i-1]的解码方式数为num[i-1],要求s[0]~s[i]的解码方式数num[i]:

跟上面一样有:num[i] = num[i-2] * canDecoded(s[i-1]~s[i]) + num[i-1] * canDecoded(s[i]),

然后需要进行分类讨论:

(1)如果s[i] < ‘0‘ || s[i] > ‘9‘,那么num[i] = 0;

(2)如果s[i] == ‘0‘,那么canDecoded(s[i]) = 0,要使canDecoded(s[i-1]~s[i]) = 1,s[i-1]必须为‘1‘或者‘2‘,于是num[i] = num[i-2],否则num[i] = 0;

(3)如果s[i] >= ‘1‘ && s[i] <= ‘9‘,那么就必须看s[i-1]了:

        (3.1)如果s[i-1] == ‘1‘,那么num[i] = num[i-2] * 1 + num[i-1] * 1 = num[i-2] + num[i-1];

        (3.2)如果s[i-1] == ‘2‘,那么num[i] = num[i-2] * (s[i]为1~6) + num[i-1] * 1;

        (3.3)如果s[i-1] >= ‘3‘,那么num[i] = num[i-2] * 0 + num[i-1] * 1 = num[i-1];

注意:上面没有处理边界条件。

下面,就来看看实现代码吧:

class Solution {
public:
	int numDecodings(string s)
	{
		if(s.size() == 0) {
			return 0;
		}

		vector<int> num;
		num.resize(s.size());

		size_t i = 0;
		for(i = 0; i < s.size(); ++i) {
			if(s[i] < '0' || s[i] > '9') {
				num[i] = 0;
				return 0;
			}
			if(i == 0) {
				if(s[i] == '0') {
					num[i] = 0;
					return 0;
				}
				else {
					num[i] = 1;
				}
				continue;
			}
			if(s[i] == '0') {
				if(s[i-1] == '1' || s[i-1] == '2') {
					num[i] = (i >= 2 ? num[i-2] : 1);
				}
				else {
					num[i] = 0;
					return 0;
				}
			}
			else {
				if(s[i-1] == '1' || s[i-1] == '2' && s[i] >= '1' && s[i] <= '6') {
					num[i] = (i >= 2 ? num[i-2] : 1) + num[i-1];
				}
				else {
					num[i] = num[i-1];
				}
			}
		}

		return num[i-1];
	}
};