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.
Build confidence with an intuition-first walkthrough focused on array fundamentals.
Given an array of string words, return all strings in words that are a substring of another word. You can return the answer in any order.
Example 1:
Input: words = ["mass","as","hero","superhero"] Output: ["as","hero"] Explanation: "as" is substring of "mass" and "hero" is substring of "superhero". ["hero","as"] is also a valid answer.
Example 2:
Input: words = ["leetcode","et","code"] Output: ["et","code"] Explanation: "et", "code" are substring of "leetcode".
Example 3:
Input: words = ["blue","green","bu"] Output: [] Explanation: No string of words is substring of another string.
Constraints:
1 <= words.length <= 1001 <= words[i].length <= 30words[i] contains only lowercase English letters.words are unique.Problem summary: Given an array of string words, return all strings in words that are a substring of another word. You can return the answer in any order.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · String Matching
["mass","as","hero","superhero"]
["leetcode","et","code"]
["blue","green","bu"]
substring-xor-queries)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1408: String Matching in an Array
class Solution {
public List<String> stringMatching(String[] words) {
List<String> ans = new ArrayList<>();
int n = words.length;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (i != j && words[j].contains(words[i])) {
ans.add(words[i]);
break;
}
}
}
return ans;
}
}
// Accepted solution for LeetCode #1408: String Matching in an Array
func stringMatching(words []string) []string {
ans := []string{}
for i, w1 := range words {
for j, w2 := range words {
if i != j && strings.Contains(w2, w1) {
ans = append(ans, w1)
break
}
}
}
return ans
}
# Accepted solution for LeetCode #1408: String Matching in an Array
class Solution:
def stringMatching(self, words: List[str]) -> List[str]:
ans = []
for i, s in enumerate(words):
if any(i != j and s in t for j, t in enumerate(words)):
ans.append(s)
return ans
// Accepted solution for LeetCode #1408: String Matching in an Array
impl Solution {
pub fn string_matching(words: Vec<String>) -> Vec<String> {
let mut ans = Vec::new();
let n = words.len();
for i in 0..n {
for j in 0..n {
if i != j && words[j].contains(&words[i]) {
ans.push(words[i].clone());
break;
}
}
}
ans
}
}
// Accepted solution for LeetCode #1408: String Matching in an Array
function stringMatching(words: string[]): string[] {
const ans: string[] = [];
const n = words.length;
for (let i = 0; i < n; ++i) {
for (let j = 0; j < n; ++j) {
if (words[j].includes(words[i]) && i !== j) {
ans.push(words[i]);
break;
}
}
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
At each of the n starting positions in the text, compare up to m characters with the pattern. If a mismatch occurs, shift by one and restart. Worst case (e.g., searching "aab" in "aaaa...a") checks m characters at nearly every position: O(n × m).
KMP and Z-algorithm preprocess the pattern in O(m) to build a failure/Z-array, then scan the text in O(n) — never backtracking. Total: O(n + m). Rabin-Karp uses rolling hashes for O(n + m) expected time. All beat the O(n × m) brute force of checking every position.
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.