Given an array of strings words
(without duplicates), return all the 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 (not necessarily distinct) in the given array.
Example 1:
Input: words = ["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".
Example 2:
Input: words = ["cat","dog","catdog"] Output: ["catdog"]
Constraints:
1 <= words.length <= 104
1 <= words[i].length <= 30
words[i]
consists of only lowercase English letters.words
are unique.1 <= sum(words[i].length) <= 105
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to concatenated words is to try every single way to build a longer word by combining smaller words from the given list. We check each possibility to see if it matches the target word.
Here's how the algorithm would work step-by-step:
def find_concatenated_words_brute_force(words):
concatenated_words = []
for target_word in words:
if is_concatenated(target_word, words):
concatenated_words.append(target_word)
return concatenated_words
def is_concatenated(target_word, word_list):
if not target_word:
return False
for i in range(1, len(target_word) + 1):
prefix = target_word[:i]
# Need to avoid infinite recursion with the same word
if prefix in word_list and prefix != target_word:
suffix = target_word[i:]
# Check if the suffix is either a word or can be concatenated
if not suffix or suffix in word_list or is_concatenated(suffix, word_list):
return True
return False
The goal is to find all the words in a list that can be formed by concatenating other words from the same list. We'll use a dynamic programming and a set lookup to efficiently check if a word can be constructed from smaller words in the list.
Here's how the algorithm would work step-by-step:
def find_concatenated_words(words):
word_set = set(words)
concatenated_words = []
def can_form(word):
if not word:
return True
# Use DP to avoid redundant calculations
dp = [False] * (len(word) + 1)
dp[0] = True
for i in range(1, len(word) + 1):
for j in range(i):
# Check if prefix exists and remaining can be formed
if dp[j] and word[j:i] in word_set:
dp[i] = True
break
return dp[len(word)]
# Iterate through words, check if concatenated
for word in words:
word_set.remove(word)
if can_form(word):
concatenated_words.append(word)
# Restore the word set for subsequent checks
word_set.add(word)
return concatenated_words
Case | How to Handle |
---|---|
Empty input word list | Return an empty list immediately as there are no words to concatenate. |
Word list containing an empty string | Handle empty strings gracefully by either skipping them during concatenation or defining their contribution as null depending on context; this prevents errors caused by empty string length operations or incorrect substring calculations. |
Word list with a single very long word | This might cause the DP or Trie-based search space to be very large so consider limiting the maximum word length to a reasonable value to prevent excessive memory allocation and runtime issues. |
A word is a concatenation of itself (e.g., 'abab' from ['ab', 'ab']) | Ensure the solution handles self-concatenation correctly, potentially requiring a check that the word isn't solely formed by repeating the same element or is equal to an existing word. |
Very large input word list size causing memory limits | Employ space optimization techniques or consider the use of external memory data structures if the word list is too large to fit into memory. |
A word is a prefix of another word (e.g., 'bar' and 'barfoo') | Prefix/suffix relationships can lead to incorrect concatenation detection, so explicitly verify that the full string has been formed during the concatenation process to avoid partial word matches being considered complete concatenations. |
Deep recursion with very long words, potentially leading to stack overflow | Convert recursive solutions to iterative ones to avoid stack overflow errors, or implement tail-call optimization if the language supports it. |
Word list contains extremely long words near the allowed memory limit. | Check for integer overflows in calculations related to string lengths or memory allocations, and use appropriate data types or guard conditions. |