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.
You are given an array arr of size n consisting of non-empty strings.
Find a string array answer of size n such that:
answer[i] is the shortest substring of arr[i] that does not occur as a substring in any other string in arr. If multiple such substrings exist, answer[i] should be the lexicographically smallest. And if no such substring exists, answer[i] should be an empty string.Return the array answer.
Example 1:
Input: arr = ["cab","ad","bad","c"] Output: ["ab","","ba",""] Explanation: We have the following: - For the string "cab", the shortest substring that does not occur in any other string is either "ca" or "ab", we choose the lexicographically smaller substring, which is "ab". - For the string "ad", there is no substring that does not occur in any other string. - For the string "bad", the shortest substring that does not occur in any other string is "ba". - For the string "c", there is no substring that does not occur in any other string.
Example 2:
Input: arr = ["abc","bcd","abcd"] Output: ["","","abcd"] Explanation: We have the following: - For the string "abc", there is no substring that does not occur in any other string. - For the string "bcd", there is no substring that does not occur in any other string. - For the string "abcd", the shortest substring that does not occur in any other string is "abcd".
Constraints:
n == arr.length2 <= n <= 1001 <= arr[i].length <= 20arr[i] consists only of lowercase English letters.Problem summary: You are given an array arr of size n consisting of non-empty strings. Find a string array answer of size n such that: answer[i] is the shortest substring of arr[i] that does not occur as a substring in any other string in arr. If multiple such substrings exist, answer[i] should be the lexicographically smallest. And if no such substring exists, answer[i] should be an empty string. Return the array answer.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Trie
["cab","ad","bad","c"]
["abc","bcd","abcd"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
class Solution {
public String[] shortestSubstrings(String[] arr) {
int n = arr.length;
String[] ans = new String[n];
Arrays.fill(ans, "");
for (int i = 0; i < n; ++i) {
int m = arr[i].length();
for (int j = 1; j <= m && ans[i].isEmpty(); ++j) {
for (int l = 0; l <= m - j; ++l) {
String sub = arr[i].substring(l, l + j);
if (ans[i].isEmpty() || sub.compareTo(ans[i]) < 0) {
boolean ok = true;
for (int k = 0; k < n && ok; ++k) {
if (k != i && arr[k].contains(sub)) {
ok = false;
}
}
if (ok) {
ans[i] = sub;
}
}
}
}
}
return ans;
}
}
// Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
func shortestSubstrings(arr []string) []string {
ans := make([]string, len(arr))
for i, s := range arr {
m := len(s)
for j := 1; j <= m && len(ans[i]) == 0; j++ {
for l := 0; l <= m-j; l++ {
sub := s[l : l+j]
if len(ans[i]) == 0 || ans[i] > sub {
ok := true
for k, t := range arr {
if k != i && strings.Contains(t, sub) {
ok = false
break
}
}
if ok {
ans[i] = sub
}
}
}
}
}
return ans
}
# Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
class Solution:
def shortestSubstrings(self, arr: List[str]) -> List[str]:
ans = [""] * len(arr)
for i, s in enumerate(arr):
m = len(s)
for j in range(1, m + 1):
for l in range(m - j + 1):
sub = s[l : l + j]
if not ans[i] or ans[i] > sub:
if all(k == i or sub not in t for k, t in enumerate(arr)):
ans[i] = sub
if ans[i]:
break
return ans
// Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
// class Solution {
// public String[] shortestSubstrings(String[] arr) {
// int n = arr.length;
// String[] ans = new String[n];
// Arrays.fill(ans, "");
// for (int i = 0; i < n; ++i) {
// int m = arr[i].length();
// for (int j = 1; j <= m && ans[i].isEmpty(); ++j) {
// for (int l = 0; l <= m - j; ++l) {
// String sub = arr[i].substring(l, l + j);
// if (ans[i].isEmpty() || sub.compareTo(ans[i]) < 0) {
// boolean ok = true;
// for (int k = 0; k < n && ok; ++k) {
// if (k != i && arr[k].contains(sub)) {
// ok = false;
// }
// }
// if (ok) {
// ans[i] = sub;
// }
// }
// }
// }
// }
// return ans;
// }
// }
// Accepted solution for LeetCode #3076: Shortest Uncommon Substring in an Array
function shortestSubstrings(arr: string[]): string[] {
const n: number = arr.length;
const ans: string[] = Array(n).fill('');
for (let i = 0; i < n; ++i) {
const m: number = arr[i].length;
for (let j = 1; j <= m && ans[i] === ''; ++j) {
for (let l = 0; l <= m - j; ++l) {
const sub: string = arr[i].slice(l, l + j);
if (ans[i] === '' || sub.localeCompare(ans[i]) < 0) {
let ok: boolean = true;
for (let k = 0; k < n && ok; ++k) {
if (k !== i && arr[k].includes(sub)) {
ok = false;
}
}
if (ok) {
ans[i] = sub;
}
}
}
}
}
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.