用python写leetcode【4】 --单词搜索 II(212) (dfs+前缀树/字典树)


今天写了一道差点把我心太搞炸的题目 单词搜索 II(212),其中关于前缀树的方法参考 前缀树
答案直接代码三,代码一代码二是错误示范

单词搜索 II(212)

题目

单词搜索 II
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例:

输入:
words = [“oath”,“pea”,“eat”,“rain”] and board =
[
[‘o’,‘a’,‘a’,‘n’],
[‘e’,‘t’,‘a’,‘e’],
[‘i’,‘h’,‘k’,‘r’],
[‘i’,‘f’,‘l’,‘v’]
]

输出: [“eat”,“oath”]
说明:
你可以假设所有输入都由小写字母 a-z 组成。

提示:

你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)。

过程

1、首先想到的是使用dfs,对每一个输入的单词,采用dfs搜索在board中是否可以获得相应的排序,由于题干中提到用前缀树,因此先把words列表从长到短排序,在判断好每一个单词后,将单词加入前缀树,后续每一个单词首先判断是否是某一个前缀,如果不是再dfs,节省时间。代码如代码一所示,但是!!!在第34个例子中卡住了,时间超时。

2、于是借鉴了网上的思路,将单词一开始全部输入到一个前缀树中,之后任然使用dfs的方法,此时dfs不是判断board当前字符是否在某个单词中,而是判断是否在当前树节点中有子节点。按照该思路,编写了代码二,但是!!!!!在第35个例子中卡住了,超时!但是这就是网上的思路,后来又思考可以在每次搜索到一个单词后在前缀树中删除单词,这样保证不被多次搜索,任然不行。最后使用了别人的代码,然后在答案中找到了代码三,又简洁明了又快速。

!!想要答案的直接代码三

代码一

    class Solution:
        def isPalindrome(self, s):
            """
            :type s: str
            :rtype: bool
            """
            alphanumeric = re.sub("[^A-Za-z0-9]+", "", s).lower()
            #正则匹配
            print(alphanumeric)
            return alphanumeric == alphanumeric[::-1]


    class Solution:
        def isPalindrome(self, s: 'str') -> 'bool':
            s = [c for c in s if c .isalpha() or c.isdigit()]
            if len(s) <= 1:
                return True
            i, j = 0, len(s) - 1
            while i < j:
                if s[i].lower() != s[j].lower():
                    return False
                i, j = i + 1, j - 1
            return True




    import copy
    class Trie:
        def __init__(self):
            """
            Initialize your data structure here.
            """
            self.root = {}
            
     
        def insert(self, word):
            """
            Inserts a word into the trie.
            :type word: str
            :rtype: void
            """
            node = self.root
            for c in word:
                if  c not in node:
                    node[c] = {}
                node = node[c]
              
            node['flag'] = True
     
        def search(self, word):
            """
            Returns if the word is in the trie.
            :type word: str
            :rtype: bool
            """
            node = self.root
            for c in word:
                if not c in node:
                    return False
                node = node[c]
                
            # Doesn't end here
            if 'flag'  not in node:
                return False
            
            return True
     
        def startsWith(self, prefix):
            """
            Returns if there is any word in the trie that starts with the given prefix.
            :type prefix: str
            :rtype: bool
            """
            node = self.root
            for c in prefix:
                if not c in node:
                    return False
                node = node[c]
            
            return True
        class Solution:
            def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
                
                ans =[]
                xlen = len(board[0])
                ylen = len(board)
                ptree = Trie()
               
                def dfs(word,i,j,aflag):
                    bflag = copy.deepcopy(aflag)
                    #print(word,i,j,ans,bflag)
                    if bflag[i][j]==1 or word[0]!=board[i][j]:
                        
                        return False
                    else:
                        bflag[i][j]=1
                        if len(word)==1:
                            return True
                    if i>0:
                        if dfs(word[1:],i-1,j,bflag):
                            return True
                    if j<xlen-1:
                        if dfs(word[1:],i,j+1,bflag):
                            return True
                    if j>0:
                        if dfs(word[1:],i,j-1,bflag):
                            return True
                    if i<ylen-1:
                        if dfs(word[1:],i+1,j,bflag):
                            return True
                    return False
                words.sort(key=lambda x:len(x))
                for word in words:
                   # print(ans,word,words)
                    flag = 0
                    if ptree.startsWith(word) :
                        if ptree.search(word):
                            pass
                        else:
                            ans.append(word)
                    else:
                        f = word[0]
                        bflag = []
                        for i in range(0, ylen):
                            tmp = []
                            for j in range(0, xlen):
                                tmp.append([0])
                            bflag.append(tmp)
                            
                        for i in range(len(board)):
                            if flag == 1:
                                break
                            for j in range(len(board[0])):
                                print(i,j)
                                if dfs(word,i,j,bflag):
                                    ptree.insert(word)
                                    ans.append(word)
                                    flag = 1
                                    break
                                
                
                return ans

代码二

    import copy
    class Trie:
        def __init__(self):
            """
            Initialize your data structure here.
            """
            self.root = {}
            
     
        def insert(self, word):
            """
            Inserts a word into the trie.
            :type word: str
            :rtype: void
            """
            node = self.root
            for c in word:
                if  c not in node:
                    node[c] = {}
                node = node[c]
              
            node['flag'] = True
    class Solution:
        def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
            
            ans =[]
            xlen = len(board[0])
            ylen = len(board)
            ptree = Trie()
            for word in words:
                ptree.insert(word)
            
           
            def dfs(c,i,j,aflag,word):
                bflag = copy.deepcopy(aflag)
                #print(word,i,j,ans,c) 
                if bflag[i][j]==1 or board[i][j] not in c:              
                    return False
                else:
                    newc = c[board[i][j]]
                    if 'flag' in newc:
                        temp = word+board[i][j]
                        if temp not in ans:
                            ans.append(temp)
                    bflag[i][j]=1
                if i>0:
                    if dfs(newc,i-1,j,bflag,word+board[i][j]):
                        return True
                if j<xlen-1:
                    if dfs(newc,i,j+1,bflag,word+board[i][j]):
                        return True
                if j>0:
                    if dfs(newc,i,j-1,bflag,word+board[i][j]):
                        return True
                if i<ylen-1:
                    if dfs(newc,i+1,j,bflag,word+board[i][j]):
                        return True
                return False
            
           
            wor = ''
            bflag=[]
            for i in range(0, ylen):
                tmp = []
                for j in range(0, xlen):
                    tmp.append(0)
                bflag.append(tmp)
                
            for i in range(len(board)):
                for j in range(len(board[0])):
                    #print(i,j)
                    dfs(ptree.root,i,j,bflag,wor)
     
        return ans

代码三

     class Trie(object):
        def __init__(self):
            self.root = {}       
        
        def insert(self, word):
            """
            Inserts a word into the trie.
            :type word: str
            :rtype: void
            """
            curNode = self.root
            for i in word:
                if i not in curNode:
                    curNode[i] = {}
                curNode = curNode[i]
            curNode["#"] = True

        class Solution:
            
            def findWords(self, board, words):
                """
                :type board: List[List[str]]
                :type words: List[str]
                :rtype: List[str]
                """
                row = len(board)
                colum = len(board[0])
                res = []
        
                def find(x, y, word, TrieNode):
                    if x >= 0 and x < row and y >= 0 and y < colum and board[x][y] in TrieNode:
                        TrieNode = TrieNode[board[x][y]]
                        word += board[x][y]
                        if TrieNode.get("#", 9) == True:
                            res.append(word)
                        t = board[x][y]
                        board[x][y] = 3
                        find(x + 1, y, word, TrieNode)
                        find(x - 1, y, word, TrieNode)
                        find(x, y + 1, word, TrieNode)
                        find(x, y - 1, word, TrieNode)
                        board[x][y] = t
        
                root = Trie()
                tmp = set()
                for i in words:
                    root.insert(i)
                    tmp.add(i[0])
                for i in range(row):
                    for j in range(colum):
                        if board[i][j] in tmp:
                            find(i, j, "", root.root)
        
                return list(set(res))

总结

将代码单独放入spyder中运行,记录运行时间,发现时间差了约100倍。
总结了别人代码的优点

  1. 为了判断在dfs中是否已经使用过board中的某个数,我重新定义了一个board,用来记录是否被使用,而代码三中直接将使用过的位置赋值为数字传入下一级的dfs,并在返回之后重新改回来,这样就少了一个巨大的数组的传递和查找
  2. 代码三在处理前就先把words中重复的删去,并且最后用了 list(set(res))来删除重复的,比我每一次判断效率高
  3. 代码3 将字符位置在board边角的情况放到了每一个函数内部处理,显得简洁。
  4. 使用了TrieNode.get("#", 9)这种方法,简洁

其中主要是因为第一个问题,所以还是要注意闭包的使用!!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值