- define TrieNode, Trie
- build Trie based on words
- loop whole board, start with random i,j to search Trie using dfs(backtracking)
- if a word exist, update res and reset mark into None (avoid duplicate). then continue backtracking ```python class Trie: def init(self,val=None): self.val = val self.children = {} self.isWord = False
class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
#build trie
root = Trie()
for w in words:
cur = root
for c in w:
if c not in cur.children:
node = Trie(c)
cur.children[c] = node
cur = cur.children[c]
cur.isWord = True
# def search(self,wrd):
# cur = root
# for c in wrd:
# if c in cur.children:
# cur = cur.children[c]
# else:
# return False
# return cur.isWord
def btr(x,y,path,bd,node,res):
if x < 0 or y <0 or x >= len(bd) or y >= len(bd[0]):
return
ch = bd[x][y]
if ch not in node.children or ch == '#':
return
node = node.children[ch]
if node.isWord == True:
res.append(''.join(path+[ch]))
node.isWord = False # avoid duplicate
bd[x][y]= '#'
btr(x-1,y,path+[ch],bd,node,res)
btr(x+1,y,path+[ch],bd,node,res)
btr(x,y-1,path+[ch],bd,node,res)
btr(x,y+1,path+[ch],bd,node,res)
bd[x][y] = ch
# try different start point to dfs/ backtracking
r,c = len(board),len(board[0])
res = []
cur = root
for i in range(r):
for j in range(c):
btr(i,j,[],board,cur,res)
return res ```
optimization: instead of using isWord at TrieNode to mark end of each word, we can directly cache each word at end of TrieNode. As a result, we don’t have to save intermediate result path and save time for copy path into res.
class TrieNode:
def __init__(self,val=None):
self.v = val
self.children = {}
self.word = '' # mark the complete word saved here, since input is not going to be changed. otherwise, we can't do so.
"""
@desc:build a trie based on given words
@ insert
@ search
"""
class Trie:
def __init__(self):
self.root = TrieNode()
def build(self,words):
""" O(N*W) /O(N*W) N: words; W: length of word;
"""
for wd in words:
cur = self.root
for c in wd:
if c not in cur.children:
cur.children[c] = TrieNode(c)
cur = cur.children[c]
cur.word = wd
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
res = []
trie = Trie()
trie.build(words)
cur = trie.root
def dfs(i,j,bd,node,res):
""" O(RC)/O(RC)
"""
if i < 0 or j < 0 or i >= len(bd) or j >= len(bd[0]):
return
# be careful of the order: (1) check if out of bound (2) check if bd[i][j] in node.children (3) append matched word into res
ch = bd[i][j]
if ch not in node.children:
return
node = node.children[ch]
if node.word:
#print(node.word)
res.append(node.word)
node.word = None # deduplicate
bd[i][j] = '*'
dfs(i+1,j,bd,node,res)
dfs(i-1,j,bd,node,res)
dfs(i,j+1,bd,node,res)
dfs(i,j-1,bd,node,res)
bd[i][j] = ch
r = len(board)
c = len(board[0])
for i in range(r):
for j in range(c):
dfs(i,j,board,cur,res)
return res
DFS
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
res = []
def dfs(i,j,seen,bd,word,pos):
if pos == len(word):
res.append(word)
seen.add((i,j))
cur = bd[i][j]
for dx,dy in [(-1,0),(1,0),(0,-1),(0,1)]:
x = i + dx
y = j + dy
if 0 <= x < len(bd) and 0 <= y < len(bd[0]) and (x,y) not in seen:
if pos < len(word) and word[pos] == bd[x][y]:
dfs(x,y,seen,bd,word,pos+1)
for i in range(len(board)):
for j in range(len(board[0])):
for w in words:
if w[0] == board[i][j]:
dfs(i,j,set(),board,w,1)
return list(set(res))