刷力扣随机题

 

目录

题目及其难度:

问题描述

示例:

解决方案:

暴力解(运行会超时)

思路:

代码:

矩阵压缩加上寻找子集:

思路:

完整代码:


        终于随机刷到了一道困难的题。做了几天(有空的时候做一下),不用一点技巧做不出来,参考了一下大佬们的思路,自己有重新写一遍,通过这一次的题,感觉到了用深度优先算法,和暴力的差距。可能要用一段时间去学习没有熟练掌握的算法了。

题目及其难度:

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’删除)

例如:

刷力扣随机题_第1张图片

 高手的压缩矩阵(位运算进行转换):细节讲解请移步至   链接     ,目前还不熟练就还没有使用。

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)

你可能感兴趣的:(算法,leetcode,算法)