472.连接词
题目描述
思路
字典树+DFS
判断一个词是否是连接词,需要判断单词是否完全由至少两个给定数组中的更短的非空单词组成(可以重复)。判断更短的单词是否在给定的数组中,可以用字典树实现。
可以先将words按照字符串的长度递增顺序排序,排序后可以确保当遍历到任意单词时,比该单词短的单词一定都已经遍历过,因此可以根据已经遍历过的全部单词判断当前单词是不是连接词。
将words排序后,遍历数组,跳过空字符串,对于每个非空单词,判断该单词是不是连接词,如果是连接词就将该单词加入结果数组,如果不是就放入字典树。
判断一个单词是不是连接词的做法是在字典树中DFS。从该单词的第一个字符(即下标为0处的字符)开始,在字典树中依次搜索每个字符对应的结点,可能有以下几种情况:
- 如果一个字符对应的结点是单词的结尾,则找到了一个更短的单词,从该字符的后一个字符开始搜索下一个更短的单词;
- 如果一个字符对应的结点在字典树中不存在,则当前的搜索结果失败,回到上一个单词的结尾继续搜索。
如果找到一个更短的单词且这个更短的单词的最后一个字符是当前单词的最后一个字符,则当前单词是连接词。由于words中没有重复你的单词,因此在判断一个单词是不是连接词时,该单词一定没有加入字典树,由此可以确保判断连接词的条件成立。
由于一个连接词由多个更短的非空单词组成,如果存在一个较长的连接词的组成部分之一是一个较短的连接词,则一定可以将这个较短的连接词换成多个更短的非空单词,因此不需要将连接词加入字典树。
Java实现
class Solution {
Trie trie = new Trie();
public List<String> findAllConcatenatedWordsInADict(String[] words) {
List<String> ans = new ArrayList<String>();
Arrays.sort(words, (a, b) -> a.length() - b.length());
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (word.length() == 0) {
continue;
}
if (dfs(word, 0)) {
ans.add(word);
} else {
insert(word);
}
}
return ans;
}
public boolean dfs(String word, int start) {
if (word.length() == start) {
return true;
}
Trie node = trie;
for (int i = start; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
node = node.children[index];
if (node == null) {
return false;
}
if (node.isEnd) {
if (dfs(word, i + 1)) {
return true;
}
}
}
return false;
}
public void insert(String word) {
Trie node = trie;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
}
class Trie {
Trie[] children;
boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
}
Python实现
class Trie:
def __init__(self):
self.children = [None] * 26
self.isEnd = False
def insert(self, word: str):
node = self
for ch in word:
ch = ord(ch) - ord('a')
if not node.children[ch]:
node.children[ch] = Trie()
node = node.children[ch]
node.isEnd = True
def dfs(self, word: str, start: int) -> bool:
if start == len(word):
return True
node = self
for i in range(start, len(word)):
node = node.children[ord(word[i]) - ord('a')]
if node is None:
return False
if node.isEnd and self.dfs(word, i + 1):
return True
return False
class Solution:
def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
words.sort(key=len)
ans = []
root = Trie()
for word in words:
if word == "":
continue
if root.dfs(word, 0):
ans.append(word)
else:
root.insert(word)
return ans