1722. Minimize Hamming Distance After Swap Operations

  • Array
  • Union Find
  • Hash Table
TIME
O(n * α(n))
SPACE
O(n)
problem on leetcode ↗source on github ↗

Intuition

If indices can swap (directly or transitively), elements at those indices can be freely rearranged. Use Union-Find to identify connected components of indices.

Solution

  1. Union-Find: Build connected components from allowedSwaps
  2. Group by component: Collect source and target values for each component
  3. Match greedily: For each component, count how many target values exist in source values
# Union-Find to group swappable indices
for a, b in allowedSwaps:
    union(a, b)

# Group indices by root
groups = defaultdict(list)
for i in range(n):
    groups[find(i)].append(i)

# For each group, count unmatched positions
hamming = 0
for indices in groups.values():
    source_count = Counter(source[i] for i in indices)
    for i in indices:
        if source_count[target[i]] > 0:
            source_count[target[i]] -= 1
        else:
            hamming += 1

Why This Works

  • Connected indices form a group where any permutation is achievable
  • For each group, we optimally match source values to target values
  • Unmatched positions contribute to Hamming distance

Complexity

  • Time: O(n × α(n)) where α is inverse Ackermann (near-constant)
  • Space: O(n)

Edge Cases

  • No allowed swaps → count mismatches directly
  • All indices connected → full flexibility
  • Duplicate values in arrays

Code

from typing import List
from collections import defaultdict, Counter


class Solution:
    def minimumHammingDistance(
        self, source: List[int], target: List[int], allowedSwaps: List[List[int]]
    ) -> int:
        """
        1722. Minimize Hamming Distance After Swap Operations
        Time: O(n * α(n))
        Space: O(n)
        """
        n = len(source)
        parent = list(range(n))
        rank = [0] * n

        def find(x: int) -> int:
            if parent[x] != x:
                parent[x] = find(parent[x])
            return parent[x]

        def union(x: int, y: int) -> None:
            px, py = find(x), find(y)
            if px == py:
                return
            if rank[px] < rank[py]:
                px, py = py, px
            parent[py] = px
            if rank[px] == rank[py]:
                rank[px] += 1

        # Build connected components
        for a, b in allowedSwaps:
            union(a, b)

        # Group indices by root
        groups = defaultdict(list)
        for i in range(n):
            groups[find(i)].append(i)

        # Count unmatched positions
        hamming = 0
        for indices in groups.values():
            source_count = Counter(source[i] for i in indices)
            for i in indices:
                if source_count[target[i]] > 0:
                    source_count[target[i]] -= 1
                else:
                    hamming += 1

        return hamming