Replace Words
In English, some words are built by sticking extra letters onto a shorter word. The shorter word is called the root . For example "cat" is the root of "cattle". You are given a list of roots and a sentence. Replace every word in the sentence with the shortest root that starts it . If no root starts the word, leave the word alone. Put every root into a trie. Then, for each word in the sentence, walk its letters down the trie and stop at the first end-of-word marker you hit — because you walk from the front, the first marker is always the shortest matching root. Words in the sentence are separated by exactly one space, and the answer is those words joined back together with single spaces.
Constraints
- 1 ≤ dictionary.length ≤ 1000
- 1 ≤ dictionaryi.length ≤ 100
- 1 ≤ sentence.length ≤ 106
- The sentence has words separated by single spaces, with no leading or trailing space
Example
dictionary = ["cat", "bat", "rat"], sentence = "the cattle was rattled by the battery""the cat was rat by the bat"Explanation "cattle" starts with the root "cat", "rattled" starts with "rat", and "battery" starts with "bat". The words "the", "was" and "by" start with no root, so they are kept as they are.
In plain terms
- Root (word root)
- A shorter word that another word is built from. Here it just means a stored word that appears at the very start of a longer word.
- Trie
- A tree of letters where each stored word is a path down from the top. Words sharing a beginning share a path.
- End-of-word marker
- A true/false flag on a node meaning "one of the stored words finishes right here".
Walking "cattle" down the trie of roots until a root ends
The roots "cat", "bat" and "rat" are stored in the trie; now handle the word "cattle".
What happens in this step
dictionary = ["cat", "bat", "rat"] word = 'cattle' node = root, prefix = '' The trie has three separate branches, one starting with c, one with b, one with r.
Steps to visualize
- The row of cells is the word being replaced, one cell per letter.
- The value shows what the trie node for that letter looks like: "node" means the path continues, "root!" means a stored root ends there, "skip" means the letter was never reached.
- The walk stops at the very first "root!" because walking from the front means the first match is always the shortest one.
- If a letter has no link in the trie, the word has no root and is kept unchanged.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
The roots "cat", "bat" and "rat" are stored in the trie; now handle the word "cattle".
What happens in this step
dictionary = ["cat", "bat", "rat"] word = 'cattle' node = root, prefix = '' The trie has three separate branches, one starting with c, one with b, one with r.
Solution
function replaceWords(dictionary, sentence) {
class TrieNode {
constructor() {
this.children = {};
this.isEnd = false;
}
}
const root = new TrieNode();
for (const word of dictionary) {
let node = root;
for (const ch of word) {
if (!node.children[ch]) {
node.children[ch] = new TrieNode();
}
node = node.children[ch];
}
node.isEnd = true;
}
const shortestRoot = (word) => {
let node = root;
let prefix = '';
for (const ch of word) {
node = node.children[ch];
if (!node) {
return word;
}
prefix += ch;
if (node.isEnd) {
return prefix;
}
}
return word;
};
return sentence.split(' ').map((word) => shortestRoot(word)).join(' ');
}- Time
- O(total letters in the roots plus total letters in the sentence)
- Space
- O(total letters in the roots)
Test cases
| Input | Expected | Covers |
|---|---|---|
dictionary = ["cat", "bat", "rat"], sentence = "the cattle was rattled by the battery" | "the cat was rat by the bat" | example from the docstring |
dictionary = ["a", "aa", "aaa"], sentence = "aaaa aaa aa a" | "a a a a" | several roots start the same word, the shortest must win |
dictionary = ["x", "y"], sentence = "hello there" | "hello there" | no root starts any word, so the sentence is unchanged |
dictionary = [], sentence = "keep every word" | "keep every word" | an empty root list leaves everything alone |
dictionary = ["cat"], sentence = "cat" | "cat" | a word that is exactly a root replaces itself |
dictionary = ["catt"], sentence = "cat" | "cat" | a root longer than the word cannot match |