Concatenated Words

We want to find out which words from words can be formed by concatenating existing words from words. One way to do this is to first build a trie from words, then perform a recursive DFS on the trie to see if a given word can be formed by tracing the trie to a completed word, starting again from the root, and tracing down to the end of another word.

Complexity is O(n*m), where n is the number of words and m is the length of a given word. The trie approach is not necessarily very cache-friendly. We also spend a considerable amount of time allocating trie nodes even though the bounds of the problem are known up front. Might be neat to figure out how to statically allocate a trie.


_63
#include <array>
_63
#include <functional>
_63
#include <string>
_63
#include <vector>
_63
_63
using namespace std;
_63
_63
struct Trie {
_63
array<Trie *, 26> next{};
_63
bool terminus = false;
_63
_63
void add_word(const string &word) {
_63
Trie *curr = this;
_63
for (char letter : word) {
_63
if (curr->next[letter - 'a'] == nullptr) {
_63
curr->next[letter - 'a'] = new Trie();
_63
}
_63
curr = curr->next[letter - 'a'];
_63
}
_63
curr->terminus = true;
_63
}
_63
_63
bool has_concatenated(const string &word) {
_63
const int n = word.length();
_63
function<bool(int, Trie *, bool)> rec = [&](int start, Trie *curr,
_63
bool hasBranched) {
_63
for (int i = start; i < n; ++i) {
_63
if (curr->next[word[i] - 'a'] == nullptr) {
_63
return false;
_63
}
_63
curr = curr->next[word[i] - 'a'];
_63
if (curr->terminus && i < n) {
_63
// only valid if we branch at least once
_63
if (i + 1 == n && hasBranched)
_63
return true;
_63
bool branch = rec(i + 1, this, true);
_63
if (branch) {
_63
return true;
_63
}
_63
}
_63
}
_63
return false;
_63
};
_63
_63
return rec(0, this, false);
_63
}
_63
};
_63
_63
class Solution {
_63
public:
_63
vector<string> findAllConcatenatedWordsInADict(vector<string> &words) {
_63
Trie root{};
_63
for (const auto &word : words) {
_63
root.add_word(word);
_63
}
_63
vector<string> concatenated;
_63
for (const auto &word : words) {
_63
if (root.has_concatenated(word))
_63
concatenated.push_back(word);
_63
}
_63
return concatenated;
_63
}
_63
};

If you found this solution helpful, consider leaving a star!