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