Minimum Operations to Make a Subsequence

hard array greedy binary search hash map

Problem

You are given target, an array of distinct integers, and another array arr (which may repeat values). In one operation you may insert any integer at any position of arr. Return the minimum number of such insertions needed so that target becomes a subsequence of arr. A subsequence keeps the original relative order but may skip elements. The answer always exists because you can always insert all of target.

Inputtarget = [1, 3, 5, 2], arr = [3, 1, 5, 2, 3]
Output1
In arr the values 1, 5, 2 already appear in target order, so they need no work. Only 3 must be inserted, giving 4 − 3 = 1 operation.

import bisect

def min_operations(target, arr):
    pos = {v: i for i, v in enumerate(target)}
    tails = []
    for x in arr:
        if x not in pos:
            continue
        idx = pos[x]
        j = bisect.bisect_left(tails, idx)
        if j == len(tails):
            tails.append(idx)
        else:
            tails[j] = idx
    return len(target) - len(tails)
function minOperations(target, arr) {
  const pos = new Map();
  target.forEach((v, i) => pos.set(v, i));
  const tails = [];
  for (const x of arr) {
    if (!pos.has(x)) continue;
    const idx = pos.get(x);
    let lo = 0, hi = tails.length;
    while (lo < hi) {
      const mid = (lo + hi) >> 1;
      if (tails[mid] < idx) lo = mid + 1;
      else hi = mid;
    }
    if (lo === tails.length) tails.push(idx);
    else tails[lo] = idx;
  }
  return target.length - tails.length;
}
class Solution {
    public int minOperations(int[] target, int[] arr) {
        Map<Integer, Integer> pos = new HashMap<>();
        for (int i = 0; i < target.length; i++) pos.put(target[i], i);
        List<Integer> tails = new ArrayList<>();
        for (int x : arr) {
            if (!pos.containsKey(x)) continue;
            int idx = pos.get(x);
            int lo = 0, hi = tails.size();
            while (lo < hi) {
                int mid = (lo + hi) >>> 1;
                if (tails.get(mid) < idx) lo = mid + 1;
                else hi = mid;
            }
            if (lo == tails.size()) tails.add(idx);
            else tails.set(lo, idx);
        }
        return target.length - tails.size();
    }
}
int minOperations(vector<int>& target, vector<int>& arr) {
    unordered_map<int, int> pos;
    for (int i = 0; i < (int)target.size(); i++) pos[target[i]] = i;
    vector<int> tails;
    for (int x : arr) {
        auto it = pos.find(x);
        if (it == pos.end()) continue;
        int idx = it->second;
        auto lb = lower_bound(tails.begin(), tails.end(), idx);
        if (lb == tails.end()) tails.push_back(idx);
        else *lb = idx;
    }
    return (int)target.size() - (int)tails.size();
}
Time: O(n log n) Space: O(n)