每天一道LeetCode-----将数字集转成字母集,计算有多少种转换方式

Decode Ways

原题链接Decode Ways

每天一道LeetCode-----将数字集转成字母集,计算有多少种转换方式_第1张图片

每一个数字和一个字母对应,总共有26个字母,对于每一个或每两个数字,都有可能将其转化成字母,计算有多少中转换形式

以1221为例,所有的转换形式为1->’A’,2->’B’,12->’L’,21->’U’,22->’V’,所有可能的转换结果是

  • ABBA ((1)(2)(2)(1))
  • ABU ((1)(2)(21))
  • AVA ((1)(22)(1))
  • LBA ((12)(2)(1))
  • LU ((12)(21))

假设给出的数字序列是s[0, 1, …, n],令dp[i - 1]表示s[0, 1, …, i-1]这个数字序列能够表示的所有转化结果个数,那么如何计算dp[i]呢

对于s[0, 1, …, i]这个数字序列的转化结果,可以分成两部分计算

  • 由s[0, 1, …, i-1] + s[i]组成,即所有转化形式是s[0, 1, …, i-1]的转换结果后加s[i]表示的字母。如1221可以表示成由122的转换结果后加(1)表示的字母,即集合[ABB, AV, LB] + [A] = [ABBA, AVA, LBA],dp的表示形式为dp[i] += dp[i - 1]
  • 由s[0, 1, …, i-2] + s[i-1, i]组成,即所有转化形式是s[0, 1, …, i-2]的转化结果后加由s[i-1, i]表示的字母。如1221可以表示成由12的转换结果后加(21)表示的字母,即集合[AB,L] + [U] = [ABU, LU],dp的表示形式为dp[i] += dp[i - 2]

所以直接动态规划计算即可,dp[i]与dp[i - 1]和dp[i - 2]有关,不过有几个边界条件,尤其注意’0’无法转换成对应字母的情况

  • s[i] == ‘0’,s[i]不能表示字母,此时dp[i]与dp[i - 1]无关
  • s[i - 1] == ‘0’,s[i-1, i]不能表示字母,此时dp[i]与dp[i - 2]无关
  • i == 0 && s[0] != ‘0’,此时dp[0] = 1
  • i == 1 && s[0] != ‘0’ && toNumber(s[0, 1]) <= 26,此时dp[1] = 2

代码如下

class Solution {
public:
    int numDecodings(string s) {
        if(s.empty())   return 0;
        vector<int> dp(s.size(), 0);
        for(int i = 0; i < s.size(); ++i)
        {
            /* 由s[0,1,...,i-1]+s[i]组成 */
            if(s[i] != '0')
                dp[i] += (i == 0) ? 1 : dp[i - 1];
            /* 由s[0,1,...,i-2]+s[i-1,i]组成 */
            if(i > 0 && s[i - 1] != '0' && ((s[i - 1] - '0') * 10 + (s[i] - '0') <= 26))
                dp[i] += (i == 1) ? 1 : dp[i - 2];
        }
        return dp[s.size() - 1];
    }
};

代码中处理了i == 0和i == 1的情况,因为i == 0时dp[i - 1]无意义,i == 1时dp[i - 2]无意义

为什么程序中是dp[i] += dp[i - 1]而不是dp[i] += 1 + dp[i - 1]

因为dp[i - 1]表示由s[0, 1, …, i-1]表示的字母集,后面追加一个字母后个数没有改变

以1221为例,假设i == 3,则s[0, 1, …, i - 1]表示的字母集是[ABB, AV, LB],个数dp[i - 1]是3

当后面追加s[i]表示的字母A时,字母集变为[ABBA, AVA, LBA],个数没变,所有dp[i] = dp[i - 1]

当然,dp[i]在初始化时都至为0,所以只需要+=操作即可


Decode Ways II

原题链接Decode Ways II

每天一道LeetCode-----将数字集转成字母集,计算有多少种转换方式_第2张图片

要求和上面一样,不同的只是数字集s中可能包含’*’,这个符号可以表示1到9任意一个数字

思想还是动态规划,但是要处理的情况变多了,比如

  • “*”可以可以表示”1”,”2”,…,”9”
  • “*2”可以表示”12”,”22”
  • “1*”可以表示”11”,”12”,…,”19”
  • “3*”不能表示任何一个二位数的数字,因为超过了”26”

对于s[0, 1, …, i]由s[0, 1, …, i-1]+s[i]组成的情况,只是简单的多出s[i] == ‘*’的情况而已

而对于s[0, 1, …, j]由s[0, 1, …, i-2]+s[i-1, i]组成的情况,s[i-1, i]需要考虑多种情况,包括

  • s[i - 1] == ‘*’ && s[i] != ‘*’
  • s[i - 1] != ‘*’ && s[i] == ‘*’
  • s[i - 1] == ‘*’ && s[i] == ‘*’
  • s[i - 1] != ‘*’ && s[i] != ‘*’

当然,无论多出多少种情况也都不能忽略i == 0和i == 1的情况

首先考虑s[0, 1, …, i]由s[0, 1, …, i-1]+s[i]组成的情况

  • 当s[i] == ‘‘时,s[i]可以表示9种数字,那么s[0, 1, …, i-1]后加s[i]时的组合个数就是dp[i - 1] 9。比如s[0, 1, …, i-1]表示的集合只有[A]一个,而s[i]可以表示’A’,’B’,…’J’九种,所以s[0, 1, …, i]表示的集合就变为[AA, AB, AC, AD, …, AJ]九种,自然是dp[i - 1] * 9
  • 当s[i] != ‘*’时,s[i]只可以表示1种数字,那么dp[i] += dp[i - 1]即可

再考虑s[0, 1, …, i]由s[0, 1, …, i-2]+s[i-1, i]组成的情况

  • 当s[i - 1] == ‘*’ && s[i] != ‘*’时
    • 如果s[i] <= ‘6’,那么有两种即”1n”和”2n”(n表示s[i])
    • 如果s[i] > ‘6’,那么只有一种”1n”(n表示s[i]),因为”2n”会超过”26”
  • 当s[i - 1] != ‘*’ && s[i] == ‘*’时
    • 如果s[i - 1] == ‘1’,那么有9种即”11”,”12”,…,”19”
    • 如果s[i - 1] == ‘2’,那么有6中即”21”,”22”,…,”26”
    • 如果s[i - 1] < ‘1’ || s[i - 1] > ‘2’,没有字母可以表示
  • 当s[i - 1] == ‘*’ && s[i] == ‘*’时
    • 有15种可能即”11”,”12”,….,”19”,”21”,”22”,…,”26”
  • 当s[i - 1] != ‘*’ && s[i] != ‘*’时
    • 如果s[i-1, i]表示的数字小于等于26,有1中可能
    • 如果s[i-1, i]表示的数字大于26,没有字母可以表示

代码如下

class Solution {
public:
    int numDecodings(string s) {
        if(s.empty())   return 0;
        long long int base = 1;
        for(int i = 0; i < 9; ++i)
            base *= 10;
        base += 7;
        vector<long long int> dp(s.size(), 0);
        for(int i = 0; i < s.size(); ++i)
        {
            if(s[i] != '0')
            {
                dp[i] += (s[i] == '*') ? ((i == 0) ? 2 : dp[i - 1] * 2) : ((i == 0) ? 1 : dp[i - 1]);
            }
            if(i > 0 && s[i - 1] != '0' && (s[i - 1] == '*' || s[i] == '*' || ((s[i - 1] - '0') * 10 + (s[i] - '0') <= 26)))
            {
                if(s[i - 1] == '*' && s[i] != '*')
                    dp[i] += (s[i] <= '6') ? ((i == 1) ? 9 : dp[i - 2] * 9) : ((i == 1) ? 1 : dp[i - 2] * 1);
                else if(s[i - 1] != '*' && s[i] == '*' && s[i - 1] <= '2')
                    dp[i] += (s[i - 1] == '1') ? ((i == 1) ? 9 : dp[i - 2] * 9) : ((i == 1) ? 6 : dp[i - 2] * 6);
                else if(s[i - 1] == '*' && s[i] == '*')
                    dp[i] += (i == 1) ? 15 : dp[i - 2] * 15;
                else if(s[i - 1] != '*' && s[i] != '*')
                    dp[i] += (i == 1) ? 1 : dp[i - 2] * 1;
            }
            dp[i] %= base;
        }
        return dp[s.size() - 1];
    }
};

好多都是用了嵌套()?:表达式,如果不容易理解可以拆开


感觉这两道题不是在考动态规划而是在考脑筋急转弯啊,各种情况想的脑袋疼

你可能感兴趣的:(LeetCode)