xinhao 2020-01-31
Given a list of words (without duplicates), please write a program that returns all concatenated words in the given list of words.
A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array.
Example:
Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; "ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".题意:在一个不重复的list中找到,找到一类单词,这个单词可以由list中的其他word来组成。在组成的过程中,每一个单词可以重复使用。思路:这题和word ladder有点像,都是进行单词的拼接。方法一:dfs对每一个单词进行dfs搜索。如果满足dfs搜索,那么在最终的res中加入这个单词。需要注意的是在dfs前回需要将word从word list中删掉,不然在后续dfs的判断中,会出错。等搜索完,再把这个单词加到word list中dfs的函数为def check(self, word, word_set)。 dfs跳出的判断条件是 if word in word_set, return True.(注意这也是为什么再最开始进入dfs循环前,我们会需要把word从word list中删掉的原因。)用一个指针i在word中进行遍历(指针的遍历范围为(1,len(word)+1),如果word[:i]在word_set中word[i:],我们对word[i:]进行dfs的判断。如果word[i:]的dfs搜索也返回的是True,那整个word我们就可以返回True时间复杂度:O(2^l*n)l是最长的单词的长度,n是单词的个数。
class Solution:
def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
res = []
word_set = set(words)
for word in word_set:
word_set.remove(word)
if self.check(word, word_set):
res.append(word)
word_set.add(word)
return res
def check(self, word, word_set):
if word in word_set:
return True
for i in range(1, len(word)+1):
if word[:i] in word_set and self.check(word[i:], word_set):
return True
return False方法二:Trie + dfs (Trie的固定code还是不太会写)
步骤:1 完成TrieNode() Trie()的类的构造
2 dfs的函数是 def dfs(self, node, word, i, space)。i指的是指针遍历在word的位置, space指的在这个单词之前是否有匹配完成另一个单词,即为两个拼接的单词中间的space。对于dfs函数来说,跳出的判断条件是i == len(word). 如果满足这个要求了,那我们返回的是return node.isEnd and space. (如果单纯只是node.isEnd是不够的,因为我们需要两个及以上的单词拼接完成一个单词,即我们肯定需要有一个space)。如果不满足这个条件,我们进行下一步判断 if node.isEnd. 如果满足这个要求了,那我们看到的是一个好兆头,因为这意味着我们至少有一个单词匹配上了,接下来需要做的是把之后的单词进行dfs搜索。 注意下一步的递归函数中,node变成, 起始的root,因为我们要重新从头开始搜索单词了。word不变,i也不变。space变成True. 如果没有满足if node.isEnd 并且我们发现word[i] not in node.children, 那我们可以直接返回False,如果在node.children中,那我们
也进入下一步搜索。node变为node.children[word[i]], i 变为i+1, space不变。
class TrieNode:
def __init__(self):
self.children = {}
self.isEnd = False
class Trie:
def __init__(self, words):
self.root = TrieNode()
for word in words:
if word:
self.insert(word)
def insert(self, word):
node = self.root
for ch in word:
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]
node.isEnd = True
class Solution:
def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
self.trie = Trie(words)
res = []
for word in words:
if self.dfs(self.trie.root, word,0, False):
res.append(word)
return res
def dfs(self, node, word, i, space):
if i == len(word):
return node.isEnd and space
if node.isEnd:
if self.dfs(self.trie.root, word, i, True):
return True
if word[i] not in node.children:
return False
else:
return self.dfs(node.children[word[i]], word, i+1, space)方法三: dp
dp[i] 代表了s[i:len(s)] 的分割情况。dp[i] = 0 意味s[i:len(s)] 不能成功的分割。dp[i] = 1 意味s[i:len(s)] 存在在word list中,但是不能成功的分割。dp[i] = 2 意味s[i:len(s)] 可以被成功得分割。
步骤:1 在主函数中对于每一个word,先构造dp = {len(word):1}. 我们对这个word进行递归搜索. def recursive(self, s, start, n)
2 在递归函数中,主要需要做的事是判断s[i:j] 是否在word set中,如果在的话,递归调用。self.recursive(s, j, n). 如果递归调用的值不是0的话,那我们可以把dp[i] 更新为2 (如果j不等于len(word),
否则dp[i]=1) 这时候记得把dp[i]就return掉,因为我们已经得到了我们想要的dp值,如果找不到这样的值,dp[i] = 0
class Solution:
def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
res = []
self.word_set = set(words)
for word in words:
self.dp = {len(word):1}
if self.check(word,0) > 1:
res.append(word)
return res
def check(self, word, start):
if start in self.dp:
return self.dp[start]
j = start + 1
while j <= len(word):
if word[start:j] in self.word_set and self.check(word,j):
self.dp[start] = 2 if j < len(word) else 1
return self.dp[start]
j += 1
self.dp[start] = 0
return self.dp[start]