動機

原來lru_cache搭配dfs可以達到判有沒有visited的效果!!

Problem

Given an m x n board of characters and a list of strings words, return all words on the board.

Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

 

Example 1:

Input: board = [[o,a,a,n],[e,t,a,e],[i,h,k,r],[i,f,l,v]], words = [oath,pea,eat,rain]Output: [eat,oath]

Example 2:

Input: board = [[a,b],[c,d]], words = [abcb]Output: []

 

Constraints:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] is a lowercase English letter.
  • 1 <= words.length <= 3 * 104
  • 1 <= words[i].length <= 10
  • words[i] consists of lowercase English letters.
  • All the strings of words are unique.

Sol

都是dfs+trie去看有沒有單字,不過要注意的是像[app,apple]不能在看到app就停下來

class Solution:
    def buildTrie(self):
        self.trie = [None, {}]
        for (i,w) in enumerate(self.ws):
            tmp = self.trie
            for c in w:
                if c not in tmp[1]:
                    tmp[1][c] = [None, {}]
                tmp = tmp[1][c]
            tmp[0] = i
    def hasChar(self,trie,c):
        if c in trie[1]:
            return trie[1][c]
        else:
            return [None, {}]
    def dfs(self,i,j, trie, pos):
        if i < 0 or i >= len(self.b) or j < 0 or j >= len(self.b[0]):
            return []
        else:
            trie2 = self.hasChar(trie,self.b[i][j])
            ret = [] if trie2[0] is None else [self.ws[trie2[0]]]
            if trie2[1]:
                dirs = [x for x in [(i+1,j),(i-1,j),(i,j+1),(i,j-1)] if x not in pos]
                for p in dirs:
                    ret += self.dfs(p[0],p[1],trie2,pos+[p])
            return ret
    def findWords(self, board: List[List[str]], ws: List[str]) -> List[str]:
        self.ws = ws
        self.buildTrie()
        #print(self.trie)
        self.b = board
        ret = []
        for i in range(len(board)):
            for j in range(len(board[0])):
                ret += self.dfs(i,j,self.trie,[(i,j)])
        return list(set(ret))

Sol2: lru_cache與標記來標visited

為了避免找重複

  • (ret): 改trie來看有沒有看過這個單字
  • (dfs的表): 走過的路就mark掉
    • backtracking常用技巧: 走過的路就mark掉,其實與改trie一樣
  • (列舉點): lru_cache避免dfs再走一次
class TrieNode:
    def __init__(self):
        self.children = defaultdict(TrieNode)
        self.isWord = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def add(self, word):
        node = self.root
        for letter in word:
            node = node.children[letter]
        node.isWord = True

class Solution:
    def buildTrie(self):
        for word in self.ws:
            self.trie.add(word)
    @lru_cache(None)
    def dfs(self,i,j, trie,cur):
        if trie.isWord:
            self.ret.append(cur)
            trie.isWord = False
        if 0 <= i < len(self.b) and 0 <= j < len(self.b[0]):
            if self.b[i][j] != '#':
                dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]
                now = self.b[i][j]
                tmpTrie = trie.children.get(now)
                if tmpTrie:
                    tmpCur = cur+now
                    self.b[i][j] = '#'
                    for (x,y) in dirs:
                        self.dfs(i+x,j+y,tmpTrie,tmpCur)
                    self.b[i][j] = now
    def findWords(self, board: List[List[str]], ws: List[str]) -> List[str]:
        self.ws = ws
        self.trie = Trie()
        self.buildTrie()
        #print(self.trie)
        self.b = board
        self.ret = []
        for i in range(len(board)):
            for j in range(len(board[0])):
                self.dfs(i,j,self.trie.root,"")
        return self.ret