目录
题目及其难度:
问题描述
示例:
解决方案:
暴力解(运行会超时)
思路:
代码:
矩阵压缩加上寻找子集:
思路:
完整代码:
终于随机刷到了一道困难的题。做了几天(有空的时候做一下),不用一点技巧做不出来,参考了一下大佬们的思路,自己有重新写一遍,通过这一次的题,感觉到了用深度优先算法,和暴力的差距。可能要用一段时间去学习没有熟练掌握的算法了。
1178.猜字谜
难度:困难
输入两组列表:单词列表,字谜列表。判断字谜列表中每一个字谜包含了多少个满足条件的单词。
条件:
word
中包含谜面 puzzle
的第一个字母。word
中的每一个字母都可以在谜面 puzzle
中找到。输入:
words = ["aaaa","asas","able","ability","actt","actor","access"],
puzzles = ["aboveyz","abrodyz","abslute","absoryz","actresz","gaswxyz"]
输出:[1,1,3,2,4,0]
解释:
1 个单词可以作为 "aboveyz" 的谜底 : "aaaa"
1 个单词可以作为 "abrodyz" 的谜底 : "aaaa"
3 个单词可以作为 "abslute" 的谜底 : "aaaa", "asas", "able"
2 个单词可以作为 "absoryz" 的谜底 : "aaaa", "asas"
4 个单词可以作为 "actresz" 的谜底 : "aaaa", "asas", "actt", "access"
没有单词可以作为 "gaswxyz" 的谜底,因为列表中的单词都不含字母 'g'。
使用了两种方法暴力方法,最开始使用的是暴力法,结果运行超时了,太大了。看了题解以后学会了第二种的矩阵压缩,并且寻找子集。虽然也是暴力解的,但是一步步过来成就感十足。
暴力解方法:没有时间限制的话,应该能够运行完,运行到9/11的时候显示运行时间超时了,给的输入数据太多了,应该也能学到些什么吧。
先将每一个puzzle的第一个字符保存(后面会将所有的puzzle和word转换成集合)为保证条件1的实现。遍历每一个puzzle,判断每一个word的集合是否是puzzle集合的子集。
class Solution:
def findNumOfValidWords(self, words: List[str], puzzles: List[str]) -> List[int]:
n=len(puzzles) # 得到字谜的长度
dic_puzzle = {} # 装每一个字谜的对应的字符
puzzles_first=[""]*n # 装入每一个字谜的第一个字符,当字谜被运算完以后,替换成每一个字谜包含的word个数 就是最后的输出结果
for i in range(n):
puzzles_first[i]=puzzles[i][0] # 将每一个字谜的第一个字符保存
dic_puzzle[puzzles[i]] = set(puzzles[i]) # 将每一个字谜转换成集合
dic_word={} # 存储每一个word所包含的字符
for word in words:
dic_word[word]=set(word) # 遍历每一个单词,将单词转换成单词中出现的字符
index=0
for puzzle in dic_puzzle: # 遍历字谜字典中的key
count=0 # 计算当前字谜包含的word个数
for word in dic_word: # 遍历每一个单词
if puzzle[0] in dic_word[word] and (0==len(dic_word[word] - dic_puzzle[puzzle])): # 当字谜的第一个字符在当前的集合中,并且A集合-B字典=0(说明A集合包含于B集合)
count+=1
puzzles_first[index]=count # 遍历完所有的word是将当前puzzle_first替换成个数
index+=1
return puzzles_first
1,将每一个word和puzzle都压缩成对应的二进制数(字符从Z-A对应26个位置上的数字,当最大的字符不是z时,二进制数的长度就不是26位)。
2,用字典保存每一个二进制数出现的次数(为了让puzzle判断word个数时不再遍历每一个word)
3,将每一个puzzle的所有子集(二进制子集)包含的word个数相加。得到最后的结果。
我的压缩矩阵:
需要使用到的方法:将字符串转换成int类型(会自动将第一次出现的1前的‘0’删除)
例如:
高手的压缩矩阵(位运算进行转换):细节讲解请移步至 链接 ,目前还不熟练就还没有使用。
freq = collections.Counter()
for word in words:
mask = 0
for c in word:
mask |= 1 << (ord(c) - ord('a'))
freq[mask] += 1
统计输入的word的二进制相同的word个数。
dic_word={} # 存储每一个二进制出现的次数所包含的字符
word_li=[set(word) for word in words] # 将每一个word转换成集合
word_li=turn(word_li) # 将每一个集合转换成二进制样式
for word_b in word_li: # 统计word列表中的每个二进制的个数
dic_word[word_b]=dic_word.get(word_b,0)+1
求集合的所有子集问题 细节请移步至上一个文章 力扣求解子集 。
def subsets(puzzle_set): # 求所有子集
sub = [[]]
for index, i in enumerate(puzzle_set[0]):
cur_len = 2 ** (index)
sub=sub+[sub[num]+[i] for num in range(cur_len)]
return sub[1:] # ['u'], ['a'], ['u', 'a'], ['l'], ['u', 'l'], ['a', 'l'],
class Solution:
def findNumOfValidWords(self, words: List[str], puzzles: List[str]) -> List[int]:
n=len(puzzles) # 得到字谜的长度
res=[0]*n
word_li=[] # 存储每一个word中的字符
puzzle_first=[0]*n # 存放每一个puzzle中的第一个字符
def turn(set_li): # 将传入的集合列表转换成对应的二进制列表# set_li 格式为[{'a'}, {'s', 'a'}, {'e', 'l', 'a', 'b'}]
for index,st in enumerate(set_li): # 遍历出每一个集合
cur_li=["0"]*26 # 创建26个字符长度的列表
for j in st: # 将集合中字符提取出来
cur_li[25-(ord(j)-ord("a"))]='1' # 将该位置的字符设置为 1
s=""
for c in cur_li:
s+=c # 将字符拼接成一个字符串
set_li[index]=int(s) # 将字符转换成数字
return set_li # [1, 1000000000000000001, 100000010011, 1000010000000100100000011]
dic_word={} # 存储每一个二进制出现的次数所包含的字符
word_li=[set(word) for word in words] # 将每一个word转换成集合
word_li=turn(word_li) # 将每一个集合转换成二进制样式
for word_b in word_li: # 统计word列表中的每个二进制的个数
dic_word[word_b]=dic_word.get(word_b,0)+1
def subsets(puzzle_set): # 求所有子集
sub = [[]]
for index, i in enumerate(puzzle_set[0]):
cur_len = 2 ** (index)
sub=sub+[sub[num]+[i] for num in range(cur_len)]
return sub[1:] # ['u'], ['a'], ['u', 'a'], ['l'], ['u', 'l'], ['a', 'l'],
for i in range(n):
puzzle_first[i]=puzzles[i][0] # # 保存puzzle字符中的第一个字符
puzzle_set=[set(puzzles[i])]
puzzle_li=subsets(puzzle_set) # 求每一个集合的所有子集
puzzle_li=turn(puzzle_li) # 将每一个puzzle子集转换成二进制
for puzzle in puzzle_li: # 遍历每一个二进制子集
str_puzzle=str(puzzle)
len_puzzle=len(str_puzzle)-1
if len_puzzle>=(ord(puzzle_first[i])-ord("a")):
if str_puzzle[len_puzzle - (ord(puzzle_first[i])-ord("a"))] =='1': # 满足第一个条件
res[i]+=dic_word.get(puzzle,0) # 将所有满足条件的子集个数相加
return res
随机题太刺激了。
技巧思路来源:状态压缩+子集,题解易懂才是最重要的 - 猜字谜 - 力扣(LeetCode)