Leetcode演算法——17、電話號碼的字元組合
題目
給定一個字串 digits,包含了2~9的整數,要求返回所有可能的數字對應的字母組合。
每一個數字都對應一些字母,和電話撥號器相對應,如下:
1 2abc 3def
4ghi 5jkl 6mno
7pqrs 8tuv 9wxyz
*+ 0 #
示例:
Input: “23”
Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
思路
1、計數法
如果字串的長度是固定的,比如 n = 3,那麼我們可以寫3個for迴圈,每個迴圈取某一位的所有可能字元,進行列舉。
但問題是長度不固定,不知道要寫幾個for迴圈。
可以重新考慮一種遍歷方法,類似於數學上的手算加法,每次在個位+1,如果溢位則需要進位。
比如:digits=‘27’,分別含有3個和4個字母,共有12種列舉情況。可以定義一個兩位數,個位滿4進1,十位滿3進1。從00開始遍歷,每次+1,直至最高位溢位為止,遍歷順序為:
[00,01,02,03,10,11,12,13,20,21,22,23]
然後提前規定好每一位的數字代表哪個字元,便可以得到12中字元組合。
等價地,為了便於編碼,可以從最高位開始+1,直至最低位溢位為止。
2、分治法
使用遞迴,將字串分為兩半,每一部分都使用同樣的分治法得到一系列字元組合,最後將這兩部分的所有組合進行笛卡爾積(兩重迴圈),就是所求結果。
這樣,就將 重迴圈化為了()次兩重迴圈,解決了不能一次性寫出 重迴圈的問題。
比如:digits = ‘2345’,則先分為 ‘23’ 和 ‘45’ 兩部分分別進行列舉,其中 ‘23’ 又可以分為 ‘2’ 和 ‘3’ 兩部分,而這兩部分的笛卡爾積可以使用雙重迴圈得到,同理 ‘45’ 也可以得到,最後將 ‘23’ 和 ‘45’ 的結果再進行一次笛卡爾積,得到了最終結果。
3、動態規劃
不斷使用笛卡爾積:前 位的遍歷結果(長度為 ),分別都加上第 位的所有可能的字元(字元個數為),就得到了前 位的遍歷結果(長度為 )。
這樣做和分治法的好處相同,將 重迴圈化為了 次兩重迴圈。
比如:digits = ‘234’,則先枚舉出第1位的所有情況,即 ‘2’ 的所有字元,然後與 ‘3’ 的所有字元進行笛卡爾積,得到了 ‘23’ 的所有情況,然後繼續與 ‘4’ 的所有字元進行笛卡爾積,得到了 ‘234’ 的所有情況。
python實現
def letterCombinations(digits):
"""
:type digits: str
:rtype: List[str]
計數法
"""
if not digits:
return []
# 獲取每一位的所有可能子母
letters = []
nums = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
for i in digits:
letters.append(nums[int(i)])
indexs = [0] * len(digits) # 初始化索引集合
result = []
while(True):
# 將當前索引對應的字母加入到結果中
tmp = ''
for i,j in enumerate(indexs):
tmp += letters[i][j]
result.append(tmp)
# 索引+1
overflow = 0
for i in range(len(indexs)):
if i == 0: # 最高位+1
indexs[0] += 1
else: # 其他位加上進位1
indexs[i] += overflow
if indexs[i] >= len(letters[i]): # 溢位,下一位進1
indexs[i] -= len(letters[i])
overflow = 1
else:
overflow = 0
if overflow == 1: # 說明最後一位也溢位了,則遍歷結束
break
return result
def letterCombinations2(digits):
"""
:type digits: str
:rtype: List[str]
分治法,遞迴。
"""
nums = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
if not digits:
return []
def fun_rec(l, u):
'''
尋找digits[l~u]之間的所有可能性
'''
# 遞迴結束條件
if l > u:
return []
if l == u:
return list(nums[int(digits[l])])
# 分為兩部分,分別得到遍歷結果,然後整合
mid = int((l + u) / 2)
result1 = fun_rec(l, mid)
result2 = fun_rec(mid+1, u)
result = []
for str1 in result1:
for str2 in result2:
result.append(str1 + str2)
return result
return fun_rec(0, len(digits)-1)
def letterCombinations3(digits):
"""
:type digits: str
:rtype: List[str]
動態規劃。
"""
if not digits:
return []
nums = ["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"]
result = []
for i in digits:
if not result:
result = list(nums[int(i)])
else:
tmp = []
for p in nums[int(i)]: # 遍歷當前位的所有字母
for exist in result: # 遍歷之前的結果
tmp.append(exist + p)
result = tmp
return result
if '__main__' == __name__:
digits = '237'
print(letterCombinations3(digits))