Minimize Hamming Distance After Swap Operations

medium union find graph

Problem

Given source, target arrays and allowedSwaps[i]=[a,b] meaning indices a and b may be swapped any number of times, return the minimum possible hamming distance after operations.

Inputsource=[1,2,3,4], target=[2,1,4,5], allowedSwaps=[[0,1],[2,3]]
Output1
Swap 0,1 ⇒ [2,1,3,4]; swap 2,3 ⇒ [2,1,4,3]; differs from target only at idx 3.

def minimum_hamming_distance(source, target, allowedSwaps):
    n = len(source)
    p = list(range(n))
    def find(x):
        while p[x] != x:
            p[x] = p[p[x]]; x = p[x]
        return x
    for a, b in allowedSwaps:
        p[find(a)] = find(b)
    from collections import defaultdict, Counter
    groups = defaultdict(list)
    for i in range(n):
        groups[find(i)].append(i)
    diff = 0
    for idxs in groups.values():
        cs = Counter(source[i] for i in idxs)
        ct = Counter(target[i] for i in idxs)
        common = sum((cs & ct).values())
        diff += len(idxs) - common
    return diff
function minimumHammingDistance(source, target, allowedSwaps) {
  const n = source.length;
  const p = [...Array(n).keys()];
  const find = x => { while (p[x] !== x) { p[x] = p[p[x]]; x = p[x]; } return x; };
  for (const [a, b] of allowedSwaps) p[find(a)] = find(b);
  const groups = new Map();
  for (let i = 0; i < n; i++) {
    const r = find(i);
    if (!groups.has(r)) groups.set(r, []);
    groups.get(r).push(i);
  }
  let diff = 0;
  for (const idxs of groups.values()) {
    const cs = new Map(), ct = new Map();
    for (const i of idxs) { cs.set(source[i], (cs.get(source[i]) || 0) + 1); ct.set(target[i], (ct.get(target[i]) || 0) + 1); }
    let common = 0;
    for (const [k, v] of cs) common += Math.min(v, ct.get(k) || 0);
    diff += idxs.length - common;
  }
  return diff;
}
class Solution {
    int[] p;
    int find(int x) { while (p[x] != x) { p[x] = p[p[x]]; x = p[x]; } return x; }
    public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
        int n = source.length;
        p = new int[n]; for (int i = 0; i < n; i++) p[i] = i;
        for (int[] e : allowedSwaps) p[find(e[0])] = find(e[1]);
        Map<Integer, List<Integer>> groups = new HashMap<>();
        for (int i = 0; i < n; i++) groups.computeIfAbsent(find(i), k -> new ArrayList<>()).add(i);
        int diff = 0;
        for (List<Integer> idxs : groups.values()) {
            Map<Integer, Integer> cs = new HashMap<>(), ct = new HashMap<>();
            for (int i : idxs) { cs.merge(source[i], 1, Integer::sum); ct.merge(target[i], 1, Integer::sum); }
            int common = 0;
            for (var e : cs.entrySet()) common += Math.min(e.getValue(), ct.getOrDefault(e.getKey(), 0));
            diff += idxs.size() - common;
        }
        return diff;
    }
}
class DSU { public: vector<int> p; DSU(int n): p(n) { iota(p.begin(), p.end(), 0); } int find(int x){ while(p[x]!=x){p[x]=p[p[x]];x=p[x];} return x; }};
int minimumHammingDistance(vector<int>& source, vector<int>& target, vector<vector<int>>& allowedSwaps) {
    int n = source.size();
    DSU d(n);
    for (auto& e : allowedSwaps) d.p[d.find(e[0])] = d.find(e[1]);
    unordered_map<int, vector<int>> groups;
    for (int i = 0; i < n; i++) groups[d.find(i)].push_back(i);
    int diff = 0;
    for (auto& [_, idxs] : groups) {
        unordered_map<int,int> cs, ct;
        for (int i : idxs) { cs[source[i]]++; ct[target[i]]++; }
        int common = 0;
        for (auto& [k, v] : cs) common += min(v, ct.count(k) ? ct[k] : 0);
        diff += (int)idxs.size() - common;
    }
    return diff;
}
Time: O((n+e)α(n)) Space: O(n)