Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
A valid encoding of an array of words is any reference string s and array of indices indices such that:
words.length == indices.lengths ends with the '#' character.indices[i], the substring of s starting from indices[i] and up to (but not including) the next '#' character is equal to words[i].Given an array of words, return the length of the shortest reference string s possible of any valid encoding of words.
Example 1:
Input: words = ["time", "me", "bell"]
Output: 10
Explanation: A valid encoding would be s = "time#bell#" and indices = [0, 2, 5].
words[0] = "time", the substring of s starting from indices[0] = 0 to the next '#' is underlined in "time#bell#"
words[1] = "me", the substring of s starting from indices[1] = 2 to the next '#' is underlined in "time#bell#"
words[2] = "bell", the substring of s starting from indices[2] = 5 to the next '#' is underlined in "time#bell#"
Example 2:
Input: words = ["t"] Output: 2 Explanation: A valid encoding would be s = "t#" and indices = [0].
Constraints:
1 <= words.length <= 20001 <= words[i].length <= 7words[i] consists of only lowercase letters.Problem summary: A valid encoding of an array of words is any reference string s and array of indices indices such that: words.length == indices.length The reference string s ends with the '#' character. For each index indices[i], the substring of s starting from indices[i] and up to (but not including) the next '#' character is equal to words[i]. Given an array of words, return the length of the shortest reference string s possible of any valid encoding of words.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Trie
["time","me","bell"]
["t"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #820: Short Encoding of Words
class Trie {
Trie[] children = new Trie[26];
}
class Solution {
public int minimumLengthEncoding(String[] words) {
Trie root = new Trie();
for (String w : words) {
Trie cur = root;
for (int i = w.length() - 1; i >= 0; i--) {
int idx = w.charAt(i) - 'a';
if (cur.children[idx] == null) {
cur.children[idx] = new Trie();
}
cur = cur.children[idx];
}
}
return dfs(root, 1);
}
private int dfs(Trie cur, int l) {
boolean isLeaf = true;
int ans = 0;
for (int i = 0; i < 26; i++) {
if (cur.children[i] != null) {
isLeaf = false;
ans += dfs(cur.children[i], l + 1);
}
}
if (isLeaf) {
ans += l;
}
return ans;
}
}
// Accepted solution for LeetCode #820: Short Encoding of Words
type trie struct {
children [26]*trie
}
func minimumLengthEncoding(words []string) int {
root := new(trie)
for _, w := range words {
cur := root
for i := len(w) - 1; i >= 0; i-- {
if cur.children[w[i]-'a'] == nil {
cur.children[w[i]-'a'] = new(trie)
}
cur = cur.children[w[i]-'a']
}
}
return dfs(root, 1)
}
func dfs(cur *trie, l int) int {
isLeaf, ans := true, 0
for i := 0; i < 26; i++ {
if cur.children[i] != nil {
isLeaf = false
ans += dfs(cur.children[i], l+1)
}
}
if isLeaf {
ans += l
}
return ans
}
# Accepted solution for LeetCode #820: Short Encoding of Words
class Trie:
def __init__(self) -> None:
self.children = [None] * 26
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
root = Trie()
for w in words:
cur = root
for c in w[::-1]:
idx = ord(c) - ord("a")
if cur.children[idx] == None:
cur.children[idx] = Trie()
cur = cur.children[idx]
return self.dfs(root, 1)
def dfs(self, cur: Trie, l: int) -> int:
isLeaf, ans = True, 0
for i in range(26):
if cur.children[i] != None:
isLeaf = False
ans += self.dfs(cur.children[i], l + 1)
if isLeaf:
ans += l
return ans
// Accepted solution for LeetCode #820: Short Encoding of Words
struct Solution;
use std::collections::HashMap;
#[derive(PartialEq, Eq, Default)]
struct Trie {
children: HashMap<char, Trie>,
end: bool,
}
impl Trie {
fn new() -> Self {
Self::default()
}
fn insert<I>(&mut self, iter: I)
where
I: Iterator<Item = char>,
{
let mut link = self;
for c in iter {
link = link.children.entry(c).or_default();
}
link.end = true;
}
fn dfs(&self, length: usize, sum: &mut usize) {
let mut is_leaf = true;
for link in self.children.values() {
is_leaf = false;
link.dfs(length + 1, sum);
}
if is_leaf {
*sum += length + 1;
}
}
}
impl Solution {
fn minimum_length_encoding(words: Vec<String>) -> i32 {
let mut trie = Trie::new();
for word in words {
trie.insert(word.chars().rev());
}
let mut res = 0;
trie.dfs(0, &mut res);
res as i32
}
}
#[test]
fn test() {
let words = vec_string!["time", "me", "bell"];
let res = 10;
assert_eq!(Solution::minimum_length_encoding(words), res);
}
// Accepted solution for LeetCode #820: Short Encoding of Words
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #820: Short Encoding of Words
// class Trie {
// Trie[] children = new Trie[26];
// }
//
// class Solution {
// public int minimumLengthEncoding(String[] words) {
// Trie root = new Trie();
// for (String w : words) {
// Trie cur = root;
// for (int i = w.length() - 1; i >= 0; i--) {
// int idx = w.charAt(i) - 'a';
// if (cur.children[idx] == null) {
// cur.children[idx] = new Trie();
// }
// cur = cur.children[idx];
// }
// }
// return dfs(root, 1);
// }
//
// private int dfs(Trie cur, int l) {
// boolean isLeaf = true;
// int ans = 0;
// for (int i = 0; i < 26; i++) {
// if (cur.children[i] != null) {
// isLeaf = false;
// ans += dfs(cur.children[i], l + 1);
// }
// }
// if (isLeaf) {
// ans += l;
// }
// return ans;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Store all N words in a hash set. Each insert/lookup hashes the entire word of length L, giving O(L) per operation. Prefix queries require checking every stored word against the prefix — O(N × L) per prefix search. Space is O(N × L) for storing all characters.
Each operation (insert, search, prefix) takes O(L) time where L is the word length — one node visited per character. Total space is bounded by the sum of all stored word lengths. Tries win over hash sets when you need prefix matching: O(L) prefix search vs. checking every stored word.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.