🌥(day57:P51)
好久没写题解文章了,今天重操旧业。
目录
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
0 <= digits.length <= 4digits[i] 是范围 ['2', '9'] 的一个数字。 电话上数字所对应的字母
⭐示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
⭐示例 2:
输入:digits = ""
输出:[]
⭐示例 3:
输入:digits = "2"
输出:["a","b","c"]
题目看起来就是简单的排列组合,暴力解题未尝不可,但是直接使用for循环解题还是过于粗暴,比如当digits是二位数可以使用两重for循环,digits为三位数可以使用三重for循环,但是我们不知道输入的是digits几位数(当然你可以使用if语句判断digits是几位数就进行几重循环)。下面我们减少粗暴,更“优雅”解题。
我们还是运用排列组合的思想,首先先取出第一个数字中所有对应的字符
如2对应的字母:
a,b,b
接着再取下一个数字
如设下一数字为3,则3对应的字母:
d,e,f
那么我们将其全排列有:
['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
接着我们又取下一个数字
如设下一数字为4,则4对应的字母:
g,h,i
然后我们将g,h,i和上一次全排列获取到的结果进行排列组合
得:['adg', 'adh', 'adi', 'aeg', 'aeh', 'aei', 'afg', 'afh', 'afi', 'bdg', 'bdh', 'bdi', 'beg', 'beh', 'bei', 'bfg', 'bfh', 'bfi', 'cdg', 'cdh', 'cdi', 'ceg', 'ceh', 'cei', 'cfg', 'cfh', 'cfi']
细心的朋友可能已经发现了,这里的排列组合其实就相当于两重for循环。将下一个数字的对应的字母和上一次全排列的结果做全排列,我们就可以解决上述题目分析中未知digits位数而不知使用几重循环的问题。
- class Solution(object):
- def letterCombinations(self, digits):
- dct = {'2': ['a', 'b', 'c'],
- '3': ['d', 'e', 'f'],
- '4': ['g', 'h', 'i'],
- '5': ['j', 'k', 'l'],
- '6': ['m', 'n', 'o'],
- '7': ['p', 'q', 'r', 's'],
- '8': ['t', 'u', 'v'],
- '9': ['w', 'x', 'y', 'z']}
- if digits == '':
- return []
- ans = ['']
- for num in digits:
- ans = [item+ap for item in ans for ap in dct[num]]
- return ans
- class Solution(object):
- def letterCombinations(self, digits):
- dct = {'2': ['a', 'b', 'c'], # 用于映射数字和字母关系的字典
- '3': ['d', 'e', 'f'],
- '4': ['g', 'h', 'i'],
- '5': ['j', 'k', 'l'],
- '6': ['m', 'n', 'o'],
- '7': ['p', 'q', 'r', 's'],
- '8': ['t', 'u', 'v'],
- '9': ['w', 'x', 'y', 'z']}
- if digits == '': # digits为空,返回空列表
- return []
- ans = [''] # 初始化元素为字符型的列表
- for num in digits: # 遍历输入的数字字符串
- # 从ans取出item,ans为上一次排列组合的所有结果,item则为其中的一种结果
- # 从dct[num]取出ap,dct[num]为当前数字对应的字母列表,ap则为其中的一个字母
- # 列表解析式将其排列组合(等效于双重for循环)
- ans = [item+ap for item in ans for ap in dct[num]]
- return ans
笛卡尔积和排列组合的思路差不多,我们使用itertools库来实现笛卡尔积
我们直观先体验笛卡尔积的使用
import itertools a = [1, 2, 3] b = [4, 5] print("a,b的笛卡尔乘积:"), for x in itertools.product(a, b): print(x) print("a自身的笛卡尔乘积:"), for x in itertools.product(a, a): print(x)输出
a,b的笛卡尔乘积:
(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
(3, 5)
a自身的笛卡尔乘积:
(1, 1)
(1, 2)
(1, 3)
(2, 1)
(2, 2)
(2, 3)
(3, 1)
(3, 2)
(3, 3)
(了解更多笛卡尔积大家可以自行搜索)
- class Solution(object):
- def letterCombinations(self, digits):
- dct = {
- "2": "abc",
- "3": "def",
- "4": "ghi",
- "5": "jkl",
- "6": "mno",
- "7": "pqrs",
- "8": "tuv",
- "9": "wxyz",
- }
- if not digits:
- return list()
- groups = (dct[digit] for digit in digits)
- return ["".join(item) for item in itertools.product(*groups)]
-
- # ["".join(item) for item in itertools.product(*groups)]
- # ans = []
- # for item in itertools.product(*groups):
- # temp = "".join(item)
- # ans.append(temp)
- # return ans
- class Solution(object):
- def letterCombinations(self, digits):
- dct = { # 数字和字母映射字典,和上面不同的是,字母不使用列表单个储存
- "2": "abc",
- "3": "def",
- "4": "ghi",
- "5": "jkl",
- "6": "mno",
- "7": "pqrs",
- "8": "tuv",
- "9": "wxyz",
- }
- if not digits: # 判断是否为空
- return list()
- groups = (dct[digit] for digit in digits) # 生成字母组合对象,如list(groups) =['abc', 'def', 'ghi']
- return ["".join(item) for item in itertools.product(*groups)] # 进行组合并返回结果
-
- # ["".join(item) for item in itertools.product(*groups)] 列表解析式等价于
- # ans = [] # 结果列表
- # for item in itertools.product(*groups): # item为('a', 'd', 'g')类型元组
- # temp = "".join(item) # 将元组中的元素提取出来拼接成字符串
- # ans.append(temp) # 将拼接好的组合字符串添加到结果列表
- # return ans
这里简要介绍itertools.product(*groups)
*是python中一个赋值的技巧,叫做解包。相信很多人都见过def func(*args, **kwargs)这种写法,在函数中,*代表不定个数的参数,以tuple的方式传入。
product(*groups)接收若干个位置参数,转换成元组tuple形式
回溯算法用于寻找所有的可行解,如果发现一个解不可行,则会舍弃不可行的解。在这道题中,由于每个数字对应的每个字母都可能进入字母组合,因此不存在不可行的解,可以直接穷举所有的解即可。
回溯主要体现再1和2,从2回溯到1
如当a和d组合后得到一个结果,到达2d位置
接着我们回溯到1a位置,a再和组合得到另一个结果
- class Solution:
- def letterCombinations(self, digits):
- dct = {
- "2": "abc",
- "3": "def",
- "4": "ghi",
- "5": "jkl",
- "6": "mno",
- "7": "pqrs",
- "8": "tuv",
- "9": "wxyz",
- }
- if not digits:
- return list()
-
- def backtrack(index):
- if index == len(digits):
- ans.append("".join(item))
- else:
- digit = digits[index]
- for letter in dct[digit]:
- item.append(letter)
- backtrack(index + 1)
- item.pop()
-
-
- item = list()
- ans = list()
- backtrack(0)
- return ans
✏代码注释
- class Solution:
- def letterCombinations(self, digits):
- dct = {
- "2": "abc",
- "3": "def",
- "4": "ghi",
- "5": "jkl",
- "6": "mno",
- "7": "pqrs",
- "8": "tuv",
- "9": "wxyz",
- }
- if not digits: # digits为空的返回
- return list()
-
- def backtrack(index): # index为下标或索引
-
- if index == len(digits): # 下标到达digits的长度说明以获取到一个完整字母组合
-
- ans.append("".join(item)) # 将该字母组合添加到结果列表
-
- else: # 如果没获取到一个完整字母组合则继续
-
- digit = digits[index] # 取下一个数字
-
- for letter in dct[digit]: # 遍历当前数字所对应的字母
-
- item.append(letter) # 将该字母添加到item中(后)形如['a', 'd']
-
- backtrack(index + 1) # 下标+1进入下一层递归
-
- item.pop() # 回溯,删除当前组合最后一个字母,回到前一步,如从['a', 'd']删除'd',回到['a']
-
- # 再进行 ['a', 'f']的组合
-
- item = list() # 用于放置单个的字母组合列表
- ans = list() # 放置所有组合列表
- backtrack(0) # 调用backtrack(0)开始回溯
- return ans # 返回结果
好了到本章到此结束,下期见
