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)
![](/qrcode.jpg)
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]