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 of strings words. For each index i in the range [0, words.length - 1], perform the following steps:
i from the words array.Return an array answer, where answer[i] is the length of the longest common prefix between the adjacent pairs after removing the element at index i. If no adjacent pairs remain or if none share a common prefix, then answer[i] should be 0.
Example 1:
Input: words = ["jump","run","run","jump","run"]
Output: [3,0,0,3,3]
Explanation:
words becomes ["run", "run", "jump", "run"]["run", "run"] having a common prefix "run" (length 3)words becomes ["jump", "run", "jump", "run"]words becomes ["jump", "run", "jump", "run"]words becomes ["jump", "run", "run", "run"]["run", "run"] having a common prefix "run" (length 3)["jump", "run", "run", "jump"]["run", "run"] having a common prefix "run" (length 3)Example 2:
Input: words = ["dog","racer","car"]
Output: [0,0,0]
Explanation:
Constraints:
1 <= words.length <= 1051 <= words[i].length <= 104words[i] consists of lowercase English letters.words[i].length is smaller than or equal 105.Problem summary: You are given an array of strings words. For each index i in the range [0, words.length - 1], perform the following steps: Remove the element at index i from the words array. Compute the length of the longest common prefix among all adjacent pairs in the modified array. Return an array answer, where answer[i] is the length of the longest common prefix between the adjacent pairs after removing the element at index i. If no adjacent pairs remain or if none share a common prefix, then answer[i] should be 0.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array
["jump","run","run","jump","run"]
["dog","racer","car"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
class Solution {
private final TreeMap<Integer, Integer> tm = new TreeMap<>();
private String[] words;
private int n;
public int[] longestCommonPrefix(String[] words) {
n = words.length;
this.words = words;
for (int i = 0; i + 1 < n; ++i) {
tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum);
}
int[] ans = new int[n];
for (int i = 0; i < n; ++i) {
remove(i, i + 1);
remove(i - 1, i);
add(i - 1, i + 1);
ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0;
remove(i - 1, i + 1);
add(i - 1, i);
add(i, i + 1);
}
return ans;
}
private void add(int i, int j) {
if (i >= 0 && i < n && j >= 0 && j < n) {
tm.merge(calc(words[i], words[j]), 1, Integer::sum);
}
}
private void remove(int i, int j) {
if (i >= 0 && i < n && j >= 0 && j < n) {
int x = calc(words[i], words[j]);
if (tm.merge(x, -1, Integer::sum) == 0) {
tm.remove(x);
}
}
}
private int calc(String s, String t) {
int m = Math.min(s.length(), t.length());
for (int k = 0; k < m; ++k) {
if (s.charAt(k) != t.charAt(k)) {
return k;
}
}
return m;
}
}
// Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
func longestCommonPrefix(words []string) []int {
n := len(words)
tm := treemap.NewWithIntComparator()
calc := func(s, t string) int {
m := min(len(s), len(t))
for k := 0; k < m; k++ {
if s[k] != t[k] {
return k
}
}
return m
}
add := func(i, j int) {
if i >= 0 && i < n && j >= 0 && j < n {
x := calc(words[i], words[j])
if v, ok := tm.Get(x); ok {
tm.Put(x, v.(int)+1)
} else {
tm.Put(x, 1)
}
}
}
remove := func(i, j int) {
if i >= 0 && i < n && j >= 0 && j < n {
x := calc(words[i], words[j])
if v, ok := tm.Get(x); ok {
if v.(int) == 1 {
tm.Remove(x)
} else {
tm.Put(x, v.(int)-1)
}
}
}
}
for i := 0; i+1 < n; i++ {
add(i, i+1)
}
ans := make([]int, n)
for i := 0; i < n; i++ {
remove(i, i+1)
remove(i-1, i)
add(i-1, i+1)
if !tm.Empty() {
if maxKey, _ := tm.Max(); maxKey.(int) > 0 {
ans[i] = maxKey.(int)
}
}
remove(i-1, i+1)
add(i-1, i)
add(i, i+1)
}
return ans
}
# Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
class Solution:
def longestCommonPrefix(self, words: List[str]) -> List[int]:
@cache
def calc(s: str, t: str) -> int:
k = 0
for a, b in zip(s, t):
if a != b:
break
k += 1
return k
def add(i: int, j: int):
if 0 <= i < n and 0 <= j < n:
sl.add(calc(words[i], words[j]))
def remove(i: int, j: int):
if 0 <= i < n and 0 <= j < n:
sl.remove(calc(words[i], words[j]))
n = len(words)
sl = SortedList(calc(a, b) for a, b in pairwise(words))
ans = []
for i in range(n):
remove(i, i + 1)
remove(i - 1, i)
add(i - 1, i + 1)
ans.append(sl[-1] if sl and sl[-1] > 0 else 0)
remove(i - 1, i + 1)
add(i - 1, i)
add(i, i + 1)
return ans
// Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
// 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 #3598: Longest Common Prefix Between Adjacent Strings After Removals
// class Solution {
// private final TreeMap<Integer, Integer> tm = new TreeMap<>();
// private String[] words;
// private int n;
//
// public int[] longestCommonPrefix(String[] words) {
// n = words.length;
// this.words = words;
// for (int i = 0; i + 1 < n; ++i) {
// tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum);
// }
// int[] ans = new int[n];
// for (int i = 0; i < n; ++i) {
// remove(i, i + 1);
// remove(i - 1, i);
// add(i - 1, i + 1);
// ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0;
// remove(i - 1, i + 1);
// add(i - 1, i);
// add(i, i + 1);
// }
// return ans;
// }
//
// private void add(int i, int j) {
// if (i >= 0 && i < n && j >= 0 && j < n) {
// tm.merge(calc(words[i], words[j]), 1, Integer::sum);
// }
// }
//
// private void remove(int i, int j) {
// if (i >= 0 && i < n && j >= 0 && j < n) {
// int x = calc(words[i], words[j]);
// if (tm.merge(x, -1, Integer::sum) == 0) {
// tm.remove(x);
// }
// }
// }
//
// private int calc(String s, String t) {
// int m = Math.min(s.length(), t.length());
// for (int k = 0; k < m; ++k) {
// if (s.charAt(k) != t.charAt(k)) {
// return k;
// }
// }
// return m;
// }
// }
// Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3598: Longest Common Prefix Between Adjacent Strings After Removals
// class Solution {
// private final TreeMap<Integer, Integer> tm = new TreeMap<>();
// private String[] words;
// private int n;
//
// public int[] longestCommonPrefix(String[] words) {
// n = words.length;
// this.words = words;
// for (int i = 0; i + 1 < n; ++i) {
// tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum);
// }
// int[] ans = new int[n];
// for (int i = 0; i < n; ++i) {
// remove(i, i + 1);
// remove(i - 1, i);
// add(i - 1, i + 1);
// ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0;
// remove(i - 1, i + 1);
// add(i - 1, i);
// add(i, i + 1);
// }
// return ans;
// }
//
// private void add(int i, int j) {
// if (i >= 0 && i < n && j >= 0 && j < n) {
// tm.merge(calc(words[i], words[j]), 1, Integer::sum);
// }
// }
//
// private void remove(int i, int j) {
// if (i >= 0 && i < n && j >= 0 && j < n) {
// int x = calc(words[i], words[j]);
// if (tm.merge(x, -1, Integer::sum) == 0) {
// tm.remove(x);
// }
// }
// }
//
// private int calc(String s, String t) {
// int m = Math.min(s.length(), t.length());
// for (int k = 0; k < m; ++k) {
// if (s.charAt(k) != t.charAt(k)) {
// return k;
// }
// }
// return m;
// }
// }
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
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.