Leetcode 1722: Minimize Hamming Distance After Swap Operations

You are given two integer arrays,
source and target, both of length n, and an array allowedSwaps containing pairs of indices where swapping is allowed. You can perform multiple swaps between the specified pairs to minimize the Hamming distance between source and target. The Hamming distance is the number of indices where the elements of source and target differ.Problem
Approach
Steps
Complexity
Input: You are given two integer arrays `source` and `target`, and an array `allowedSwaps` where each element is a pair of indices representing allowed swaps.
Example: Input: source = [1, 2, 3, 4], target = [2, 1, 4, 5], allowedSwaps = [[0, 1], [2, 3]]
Constraints:
• 1 <= n <= 10^5
• 1 <= source[i], target[i] <= 10^5
• 0 <= allowedSwaps.length <= 10^5
• allowedSwaps[i].length == 2
• 0 <= ai, bi <= n - 1
• ai != bi
Output: Return the minimum Hamming distance between `source` and `target` after performing any number of allowed swaps.
Example: Output: 1
Constraints:
• The arrays `source` and `target` have the same length `n`.
Goal: To minimize the Hamming distance, you must swap elements in `source` based on the allowed pairs of indices to make it match `target` as much as possible.
Steps:
• 1. Initialize a union-find data structure to track connected components formed by the allowed swaps.
• 2. For each component (group of indices), group the corresponding elements of `source` and `target`.
• 3. Compare the two groups and count how many elements can be matched in order to minimize the Hamming distance.
• 4. Return the minimum Hamming distance after all possible swaps.
Goal: The solution must efficiently handle the constraints for `n` and `allowedSwaps` as large as 10^5.
Steps:
• 1 <= n <= 10^5
• 1 <= source[i], target[i] <= 10^5
• 0 <= allowedSwaps.length <= 10^5
• allowedSwaps[i].length == 2
• 0 <= ai, bi <= n - 1
• ai != bi
Assumptions:
• The arrays `source` and `target` are non-empty and have the same length.
• The allowed swaps provide the flexibility to change the arrangement of `source` elements.
• Input: Input: source = [1, 2, 3, 4], target = [2, 1, 4, 5], allowedSwaps = [[0, 1], [2, 3]]
• Explanation: By swapping indices 0 and 1, and then indices 2 and 3, the array `source` becomes [2, 1, 4, 3]. This results in a Hamming distance of 1, since only the element at index 3 is different.
• Input: Input: source = [5, 1, 2, 4, 3], target = [1, 5, 4, 2, 3], allowedSwaps = [[0, 4], [4, 2], [1, 3], [1, 4]]
• Explanation: By performing the allowed swaps, the `source` array can be transformed into [1, 5, 4, 2, 3], resulting in a Hamming distance of 0, since the arrays now match exactly.
Approach: The approach involves using a union-find data structure to group indices that can be swapped and then comparing the elements at those indices in `source` and `target` to minimize the Hamming distance.
Observations:
• The allowed swaps form connected components of indices that can be freely rearranged among themselves.
• We can use union-find to efficiently group the indices that can be swapped, and then we can match the corresponding elements from `source` and `target` within each group.
Steps:
• 1. Initialize a union-find structure with each index as its own parent.
• 2. For each allowed swap, unify the two indices into the same group.
• 3. For each group, collect the elements of `source` and `target` at the indices in that group.
• 4. Compare the two collections and count how many elements match in order to minimize the Hamming distance.
• 5. Return the minimized Hamming distance.
Empty Inputs:
• An empty array is not a valid input, as the problem assumes that `source` and `target` have at least one element.
Large Inputs:
• The solution should efficiently handle inputs where `n` and `allowedSwaps.length` are as large as 10^5.
Special Values:
• If no allowed swaps are given, the result will be the original Hamming distance between `source` and `target`.
Constraints:
• The arrays `source` and `target` must have the same length.
class Solution {
vector<int> parent, rnk;
public:
int minimumHammingDistance(vector<int>& src, vector<int>& dst, vector<vector<int>>& swp) {
int n = src.size();
parent.resize(n, 0);
rnk.resize(n, 0);
for(int i = 0; i < n; i++) {
parent[i] = i;
}
for(vector<int> s: swp) {
int x = find(s[0]);
int y = find(s[1]);
if(rnk[x] < rnk[y]) {
parent[x] = y;
rnk[y]++;
} else {
parent[y] = x;
rnk[x]++;
}
}
unordered_map<int, unordered_map<int, int>> mp;
for(int i = 0; i < n; i++) {
int p = find(i);
auto &m = mp[p];
m[src[i]]++;
}
int res = 0;
for(int i = 0; i < n; i++) {
int p = find(i);
auto &m = mp[p];
if((m[dst[i]]--) <= 0) {
res += 1;
}
}
return res;
}
int find(int x) {
int y = parent[x];
if(y != x) {
parent[x] = find(y);
}
return parent[x];
}
1 : Class Definition
class Solution {
The class `Solution` is defined, which contains the function `minimumHammingDistance` that will calculate the minimum Hamming distance between two arrays after performing the allowed swap operations.
2 : Variable Declarations
vector<int> parent, rnk;
Two vectors `parent` and `rnk` are declared. `parent` will store the representative of each element in the union-find structure, and `rnk` will store the rank (or depth) of the trees in the union-find structure to keep the structure balanced.
3 : Access Control
public:
This marks the beginning of the public section of the class, making the function `minimumHammingDistance` accessible from outside the class.
4 : Function Definition
int minimumHammingDistance(vector<int>& src, vector<int>& dst, vector<vector<int>>& swp) {
The function `minimumHammingDistance` is defined. It takes three arguments: two integer vectors `src` and `dst` representing the two arrays, and a 2D vector `swp` representing the allowed swap operations between indices.
5 : Initialize n
int n = src.size();
The integer `n` is initialized to the size of the `src` array, which is assumed to be the same size as `dst`.
6 : Resize Parent Array
parent.resize(n, 0);
The `parent` array is resized to size `n` and initialized with zeros. Each element will represent its own parent initially.
7 : Resize Rank Array
rnk.resize(n, 0);
The `rnk` array is resized to size `n` and initialized with zeros. This array will track the rank of each tree in the union-find structure.
8 : Initialize Parent Array
for(int i = 0; i < n; i++) {
A loop is initiated to initialize the `parent` array, setting each element's parent to itself.
9 : Set Parent
parent[i] = i;
Each element in the `parent` array is set to point to itself, indicating that initially each element is its own representative.
10 : Swap Operation Loop
for(vector<int> s: swp) {
A loop is started to process each swap operation in the `swp` array. Each swap operation is represented as a vector of two integers.
11 : Find Roots for Swap
int x = find(s[0]);
The `find` function is called to get the root (representative) of the first element `s[0]` in the swap operation.
12 : Find Roots for Swap
int y = find(s[1]);
The `find` function is called to get the root (representative) of the second element `s[1]` in the swap operation.
13 : Union by Rank
if(rnk[x] < rnk[y]) {
If the rank of root `x` is less than that of root `y`, the tree rooted at `x` will be attached to the tree rooted at `y`.
14 : Union by Rank
parent[x] = y;
The parent of `x` is set to `y`, merging the two sets.
15 : Increase Rank
rnk[y]++;
The rank of root `y` is increased, indicating the depth of the tree has grown.
16 : Union by Rank
} else {
If the rank of root `x` is greater than or equal to that of root `y`, the tree rooted at `y` is attached to the tree rooted at `x`.
17 : Union by Rank
parent[y] = x;
The parent of `y` is set to `x`, merging the two sets.
18 : Increase Rank
rnk[x]++;
The rank of root `x` is increased.
19 : Create Map
unordered_map<int, unordered_map<int, int>> mp;
An unordered map `mp` is created to track the frequencies of elements in the `src` array within each connected component.
20 : Populate Map
for(int i = 0; i < n; i++) {
A loop is initiated to populate the map `mp` with the frequency of elements in `src`.
21 : Add Frequency
int p = find(i);
The representative `p` of the current element `i` is found.
22 : Add Frequency
auto &m = mp[p];
A reference `m` to the map of frequencies for the connected component is obtained.
23 : Add Frequency
m[src[i]]++;
The frequency of `src[i]` is incremented in the map `m` for the connected component `p`.
24 : Calculate Hamming Distance
int res = 0;
The integer `res` is initialized to 0. This will store the total Hamming distance.
25 : Update Hamming Distance
for(int i = 0; i < n; i++) {
A loop is initiated to calculate the Hamming distance.
26 : Update Hamming Distance
int p = find(i);
The representative `p` of the current element `i` is found.
27 : Update Hamming Distance
auto &m = mp[p];
A reference `m` to the map of frequencies for the connected component is obtained.
28 : Update Hamming Distance
if((m[dst[i]]--) <= 0) {
If the frequency of `dst[i]` is not sufficient to match, it indicates a mismatch, and the result is incremented.
29 : Increment Result
res += 1;
The result is incremented when the frequencies don't match.
30 : Return Result
return res;
The final Hamming distance is returned.
31 : Find Function
int find(int x) {
The `find` function is defined to find the representative (or root) of a given element `x`.
32 : Path Compression
int y = parent[x];
The parent of `x` is stored in `y`.
33 : Path Compression
if(y != x) {
If the parent of `x` is not itself, path compression is applied.
34 : Path Compression
parent[x] = find(y);
The parent of `x` is set to the representative of `y` recursively.
35 : Return Representative
return parent[x];
The representative (or root) of `x` is returned.
Best Case: O(n + m), where n is the number of elements in `source` and `target`, and m is the number of allowed swaps.
Average Case: O(n + m), as we process each swap and each element in the arrays once.
Worst Case: O(n + m), where n is the number of elements and m is the number of allowed swaps.
Description: The time complexity is linear in terms of both the number of elements and the number of allowed swaps.
Best Case: O(n), as the space used is proportional to the size of the input arrays and the union-find structure.
Worst Case: O(n + m), for storing the union-find data structure and the collections of elements in each group.
Description: The space complexity is linear in terms of the number of elements and the number of allowed swaps.
| LeetCode Solutions Library / DSA Sheets / Course Catalog |
|---|
comments powered by Disqus