
思路:字典树 + 回溯 + 记忆化搜索
- 此题的正解应该是动态规划,但是字典树也可以做
- 对于
wordDict中的每一个word,都需要直通根节点地放置在字典树当中。每个单词的地位是平等的。 - 对于目标字符串
s而言,如果读到了isEnd标志,意味着当前单词读完了,就重新从根节点开始找起,找下一个单词,不断重复这个操作,似乎可以找完整棵树。 - 但是如果出现前缀相同的情况,比如
app apple,先读到app的终结标志,直接就开始找下一个字符了,显然不对,所以需要回溯。 - 但是回溯时间复杂度指数级增长,所以需要用
failCase[i]来表示以第i位的字符开头,必然无法找到需要的单词,防止重复回溯。
代码:
class TrieNode: def __init__(self): self.isEnd = False self.children = [None for _ in range(26)] def insert(self, wordDict: List[str]) -> None: # 把所有的字符串放到同一个字典当中 # 根节点 - leet && 根节点 -- code trieRoot = self for word in wordDict: curNode = trieRoot for ch in word: chNum = ord(ch) - ord('a') if curNode.children[chNum] == None: curNode.children[chNum] = TrieNode() curNode = curNode.children[chNum] curNode.isEnd = Trueclass Solution: def __init__(self): # 为记忆化搜索做准备 self.failedCase = [False for _ in range(310)] def search(self, rootNode: TrieNode(), s: str, beginPos: int) -> bool: # 记忆化搜索,以beginPos开头的必然不在字典中,所以下次遍历到这个位置的时候直接返回 if self.failedCase[beginPos]: return False if beginPos == len(s): return True curNode = rootNode while beginPos < len(s): chNum = ord(s[beginPos]) - ord('a') if curNode.children[chNum] == None: return False curNode = curNode.children[chNum] # 回溯 if curNode.isEnd: if (self.search(rootNode, s, beginPos + 1)): return True else: self.failedCase[beginPos + 1] = True beginPos += 1 return False def wordBreak(self, s: str, wordDict: List[str]) -> bool: rootNode = TrieNode() rootNode.insert(wordDict) return self.search(rootNode, s, 0)