1722. Minimize Hamming Distance After Swap Operations - cocoder39/coco39_LC GitHub Wiki

1722. Minimize Hamming Distance After Swap Operations

2 challenges:

  1. Given index a, index b are swappable, index b and index c are swappable, then we can swap index a and index c without side effect. It means you may use b to help move value at index a to index c, and meanwhile move value at index c to index a, however value at index b can stay at index b after all the movements. This analysis drives us to consider building connection group for indexes that are inter-swappable, and UF is a good data structure for this.

Proof: Given index 0 and 1 are swappable, and index 1 and 2 are swappable

  • a, b, c
  • b, a, c (by swapping index 0 and 1)
  • b, c, a (by swapping index 1 and 2)
  • c, b, a (by swapping index 0 and 1)

Given index 0 and 1 are swappable, index 1 and 2 are swappable, index 2 and 3 are swappable, prove that index 0 and 3 are swappable

  • a, b, c, d
  • b, a, c, d (by swapping index 0, 1)
  • b, d, c, a (based on first example)
  • d, b, c, a (by swapping index 0, 1)

No matter how long the swappable chain is, we can convert the problem to the base case where there are only 3 elements.

  1. Once connection groups are built, I was stuck at how to calculate the hamming distance. building counters = collections.defaultdict(collections.Counter) so that counters[root] is the frequencies of each number under root.
counters[root][s] += 1
counters[root][t] -= 1

is equal to

        for i, s in enumerate(source):
            root = find(i)
            counters[root][s] += 1
        
        res = 0
        for i, t in enumerate(target):
            root = find(i)
            if counters[root][t] > 0:
                counters[root][t] -= 1
            else:
                res += 1
                
        return res
class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        
        def find(i):
            while parent[i] != i:
                parent[i] = parent[parent[i]]
                i = parent[i]
            return i
        
        def union(i, j):
            root_i, root_j = find(i), find(j)
            if root_i != root_j:
                if wight[root_i] > wight[root_j]:
                    parent[root_j] = root_i
                    wight[root_i] += wight[root_j]
                else:
                    parent[root_i] = root_j
                    wight[root_j] += wight[root_i]
        
        n = len(source)
        parent = [i for i in range(n)]
        wight = [1 for i in range(n)]
        for i, j in allowedSwaps:
            union(i, j)
        
        counters = collections.defaultdict(collections.Counter)
        for i, (s, t) in enumerate(zip(source, target)):
            root = find(i)
            counters[root][s] += 1
            counters[root][t] -= 1
        
        res = 0
        for root, counter in counters.items():
            for num in counter:
                if counter[num] > 0:
                    res += counter[num]
        return res