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.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given an array target that consists of distinct integers and another integer array arr that can have duplicates.
In one operation, you can insert any integer at any position in arr. For example, if arr = [1,4,1,2], you can add 3 in the middle and make it [1,4,3,1,2]. Note that you can insert the integer at the very beginning or end of the array.
Return the minimum number of operations needed to make target a subsequence of arr.
A subsequence of an array is a new array generated from the original array by deleting some elements (possibly none) without changing the remaining elements' relative order. For example, [2,7,4] is a subsequence of [4,2,3,7,2,1,4] (the underlined elements), while [2,4,2] is not.
Example 1:
Input: target = [5,1,3],arr= [9,4,2,3,4] Output: 2 Explanation: You can add 5 and 1 in such a way that makesarr= [5,9,4,1,2,3,4], then target will be a subsequence ofarr.
Example 2:
Input: target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
Output: 3
Constraints:
1 <= target.length, arr.length <= 1051 <= target[i], arr[i] <= 109target contains no duplicates.Problem summary: You are given an array target that consists of distinct integers and another integer array arr that can have duplicates. In one operation, you can insert any integer at any position in arr. For example, if arr = [1,4,1,2], you can add 3 in the middle and make it [1,4,3,1,2]. Note that you can insert the integer at the very beginning or end of the array. Return the minimum number of operations needed to make target a subsequence of arr. A subsequence of an array is a new array generated from the original array by deleting some elements (possibly none) without changing the remaining elements' relative order. For example, [2,7,4] is a subsequence of [4,2,3,7,2,1,4] (the underlined elements), while [2,4,2] is not.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Binary Search · Greedy
[5,1,3] [9,4,2,3,4]
[6,4,8,1,3,2] [4,7,6,2,3,8,6,1]
append-characters-to-string-to-make-subsequence)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1713: Minimum Operations to Make a Subsequence
class BinaryIndexedTree {
private int n;
private int[] c;
public BinaryIndexedTree(int n) {
this.n = n;
this.c = new int[n + 1];
}
public void update(int x, int v) {
for (; x <= n; x += x & -x) {
c[x] = Math.max(c[x], v);
}
}
public int query(int x) {
int ans = 0;
for (; x > 0; x -= x & -x) {
ans = Math.max(ans, c[x]);
}
return ans;
}
}
class Solution {
public int minOperations(int[] target, int[] arr) {
int m = target.length;
Map<Integer, Integer> d = new HashMap<>(m);
for (int i = 0; i < m; i++) {
d.put(target[i], i + 1);
}
List<Integer> nums = new ArrayList<>();
for (int x : arr) {
if (d.containsKey(x)) {
nums.add(d.get(x));
}
}
BinaryIndexedTree tree = new BinaryIndexedTree(m);
int ans = 0;
for (int x : nums) {
int v = tree.query(x - 1) + 1;
ans = Math.max(ans, v);
tree.update(x, v);
}
return m - ans;
}
}
// Accepted solution for LeetCode #1713: Minimum Operations to Make a Subsequence
type BinaryIndexedTree struct {
n int
c []int
}
func NewBinaryIndexedTree(n int) BinaryIndexedTree {
return BinaryIndexedTree{n: n, c: make([]int, n+1)}
}
func (bit *BinaryIndexedTree) Update(x, v int) {
for ; x <= bit.n; x += x & -x {
if v > bit.c[x] {
bit.c[x] = v
}
}
}
func (bit *BinaryIndexedTree) Query(x int) int {
ans := 0
for ; x > 0; x -= x & -x {
if bit.c[x] > ans {
ans = bit.c[x]
}
}
return ans
}
func minOperations(target []int, arr []int) int {
m := len(target)
d := make(map[int]int)
for i, x := range target {
d[x] = i + 1
}
var nums []int
for _, x := range arr {
if pos, exists := d[x]; exists {
nums = append(nums, pos)
}
}
tree := NewBinaryIndexedTree(m)
ans := 0
for _, x := range nums {
v := tree.Query(x-1) + 1
if v > ans {
ans = v
}
tree.Update(x, v)
}
return m - ans
}
# Accepted solution for LeetCode #1713: Minimum Operations to Make a Subsequence
class BinaryIndexedTree:
__slots__ = "n", "c"
def __init__(self, n: int):
self.n = n
self.c = [0] * (n + 1)
def update(self, x: int, v: int):
while x <= self.n:
self.c[x] = max(self.c[x], v)
x += x & -x
def query(self, x: int) -> int:
res = 0
while x:
res = max(res, self.c[x])
x -= x & -x
return res
class Solution:
def minOperations(self, target: List[int], arr: List[int]) -> int:
d = {x: i for i, x in enumerate(target, 1)}
nums = [d[x] for x in arr if x in d]
m = len(target)
tree = BinaryIndexedTree(m)
ans = 0
for x in nums:
v = tree.query(x - 1) + 1
ans = max(ans, v)
tree.update(x, v)
return len(target) - ans
// Accepted solution for LeetCode #1713: Minimum Operations to Make a Subsequence
struct Solution;
use std::collections::HashMap;
impl Solution {
fn min_operations(target: Vec<i32>, arr: Vec<i32>) -> i32 {
let n = target.len();
let mut idx: HashMap<i32, usize> = HashMap::new();
for x in target {
idx.insert(x, idx.len());
}
let mut dp = vec![];
for x in arr {
if let Some(&i) = idx.get(&x) {
let j = match dp.binary_search(&i) {
Ok(j) => j,
Err(j) => j,
};
if j < dp.len() {
dp[j] = i;
} else {
dp.push(i);
}
}
}
(n - dp.len()) as i32
}
}
#[test]
fn test() {
let target = vec![5, 1, 3];
let arr = vec![9, 4, 2, 3, 4];
let res = 2;
assert_eq!(Solution::min_operations(target, arr), res);
let target = vec![6, 4, 8, 1, 3, 2];
let arr = vec![4, 7, 6, 2, 3, 8, 6, 1];
let res = 3;
assert_eq!(Solution::min_operations(target, arr), res);
}
// Accepted solution for LeetCode #1713: Minimum Operations to Make a Subsequence
class BinaryIndexedTree {
private n: number;
private c: number[];
constructor(n: number) {
this.n = n;
this.c = Array(n + 1).fill(0);
}
update(x: number, v: number): void {
for (; x <= this.n; x += x & -x) {
this.c[x] = Math.max(this.c[x], v);
}
}
query(x: number): number {
let ans = 0;
for (; x > 0; x -= x & -x) {
ans = Math.max(ans, this.c[x]);
}
return ans;
}
}
function minOperations(target: number[], arr: number[]): number {
const m = target.length;
const d: Map<number, number> = new Map();
target.forEach((x, i) => d.set(x, i + 1));
const nums: number[] = [];
arr.forEach(x => {
if (d.has(x)) {
nums.push(d.get(x)!);
}
});
const tree = new BinaryIndexedTree(m);
let ans = 0;
nums.forEach(x => {
const v = tree.query(x - 1) + 1;
ans = Math.max(ans, v);
tree.update(x, v);
});
return m - ans;
}
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
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.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.
Wrong move: Locally optimal choices may fail globally.
Usually fails on: Counterexamples appear on crafted input orderings.
Fix: Verify with exchange argument or monotonic objective before committing.