每天一道LeetCode-----将数字集转成字母集,计算有多少种转换方式
Decode Ways
原題鏈接Decode Ways
每一個數(shù)字和一個字母對應,總共有26個字母,對于每一個或每兩個數(shù)字,都有可能將其轉(zhuǎn)化成字母,計算有多少中轉(zhuǎn)換形式
以1221為例,所有的轉(zhuǎn)換形式為1->’A’,2->’B’,12->’L’,21->’U’,22->’V’,所有可能的轉(zhuǎn)換結(jié)果是
- ABBA ((1)(2)(2)(1))
- ABU ((1)(2)(21))
- AVA ((1)(22)(1))
- LBA ((12)(2)(1))
- LU ((12)(21))
假設給出的數(shù)字序列是s[0, 1, …, n],令dp[i - 1]表示s[0, 1, …, i-1]這個數(shù)字序列能夠表示的所有轉(zhuǎn)化結(jié)果個數(shù),那么如何計算dp[i]呢
對于s[0, 1, …, i]這個數(shù)字序列的轉(zhuǎn)化結(jié)果,可以分成兩部分計算
- 由s[0, 1, …, i-1] + s[i]組成,即所有轉(zhuǎn)化形式是s[0, 1, …, i-1]的轉(zhuǎn)換結(jié)果后加s[i]表示的字母。如1221可以表示成由122的轉(zhuǎn)換結(jié)果后加(1)表示的字母,即集合[ABB, AV, LB] + [A] = [ABBA, AVA, LBA],dp的表示形式為dp[i] += dp[i - 1]
- 由s[0, 1, …, i-2] + s[i-1, i]組成,即所有轉(zhuǎn)化形式是s[0, 1, …, i-2]的轉(zhuǎn)化結(jié)果后加由s[i-1, i]表示的字母。如1221可以表示成由12的轉(zhuǎn)換結(jié)果后加(21)表示的字母,即集合[AB,L] + [U] = [ABU, LU],dp的表示形式為dp[i] += dp[i - 2]
所以直接動態(tài)規(guī)劃計算即可,dp[i]與dp[i - 1]和dp[i - 2]有關(guān),不過有幾個邊界條件,尤其注意’0’無法轉(zhuǎn)換成對應字母的情況
- s[i] == ‘0’,s[i]不能表示字母,此時dp[i]與dp[i - 1]無關(guān)
- s[i - 1] == ‘0’,s[i-1, i]不能表示字母,此時dp[i]與dp[i - 2]無關(guān)
- 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]表示的字母集,后面追加一個字母后個數(shù)沒有改變
以1221為例,假設i == 3,則s[0, 1, …, i - 1]表示的字母集是[ABB, AV, LB],個數(shù)dp[i - 1]是3
當后面追加s[i]表示的字母A時,字母集變?yōu)閇ABBA, AVA, LBA],個數(shù)沒變,所有dp[i] = dp[i - 1]
當然,dp[i]在初始化時都至為0,所以只需要+=操作即可
Decode Ways II
原題鏈接Decode Ways II
要求和上面一樣,不同的只是數(shù)字集s中可能包含’*’,這個符號可以表示1到9任意一個數(shù)字
思想還是動態(tài)規(guī)劃,但是要處理的情況變多了,比如
- “*”可以可以表示”1”,”2”,…,”9”
- “*2”可以表示”12”,”22”
- “1*”可以表示”11”,”12”,…,”19”
- “3*”不能表示任何一個二位數(shù)的數(shù)字,因為超過了”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種數(shù)字,那么s[0, 1, …, i-1]后加s[i]時的組合個數(shù)就是dp[i - 1] 9。比如s[0, 1, …, i-1]表示的集合只有[A]一個,而s[i]可以表示’A’,’B’,…’J’九種,所以s[0, 1, …, i]表示的集合就變?yōu)閇AA, AB, AC, AD, …, AJ]九種,自然是dp[i - 1] * 9
- 當s[i] != ‘*’時,s[i]只可以表示1種數(shù)字,那么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]表示的數(shù)字小于等于26,有1中可能
- 如果s[i-1, i]表示的數(shù)字大于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];} };好多都是用了嵌套()?:表達式,如果不容易理解可以拆開
感覺這兩道題不是在考動態(tài)規(guī)劃而是在考腦筋急轉(zhuǎn)彎啊,各種情況想的腦袋疼
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的每天一道LeetCode-----将数字集转成字母集,计算有多少种转换方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一道LeetCode-----将链表
- 下一篇: 每天一道LeetCode-----逆序链