# Beautiful Pairs

See the original problem on HackerRank.

## Solutions

For each element in $$A$$, we have to find the same element in $$B$$. This is basically like performing the intersection between $$A$$ and $$B$$.

Then, since we have to change one element in $$B$$ in any case:

• if the intersection is just $$A$$, we return A.size() - 1
• otherwise, we return inters.size() + 1

Here is the idea in C++:

  1 2 3 4 5 6 7 8 9 10  int beautifulPairs(vector& A, vector& B) { sort(begin(A), end(A)); sort(begin(B), end(B)); vector inters; set_intersection(begin(A), end(A), begin(B), end(B), back_inserter(inters)); if (inters.size() == A.size()) return inters.size() - 1; return inters.size() + 1; } 

This solution costs $$O(N \cdot logN)$$ because of using sort.

Since the domain is small we can either use a better sorting strategy (e.g. radix, counting sort) or we can just use a frequency table:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  int beautifulPairs(const vector& A, const vector& B) { array freq{}; for (auto i : B) freq[i]++; auto cnt = 0; for (auto i : A) { if (freq[i]) { freq[i]--; cnt++; } } return cnt + (cnt == A.size() ? -1 : 1); } 

This solution is linear time and constant space. Consider the tradeoff: if the domain changes we might be forced to change the solution.

In Python we can simply use Counter:

  1 2 3 4 5 6 7 8 9 10 11  def beautifulPairs(A, B): ac = Counter(A) bc = Counter(B) pairs = 0 for val in ac: if val in bc: pairs += min(ac[val],bc[val]) if pairs == len(A): return pairs - 1 else: return pairs + 1 

Rust solution with sort. Here we understand (because the language is explicit about it) that we need to modify the original arrays (or make a copy of them).

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  fn beautiful_pairs(mut a: Vec, mut b: Vec) -> usize { let mut beautiful_count: usize = 0; a.sort(); b.sort(); let mut a_iter = a.iter(); let mut b_iter = b.iter(); let mut ox = a_iter.next(); let mut oy = b_iter.next(); while let (Some(x), Some(y)) = (ox, oy) { if x == y { ox = a_iter.next(); oy = b_iter.next(); beautiful_count += 1; } else if x > y { oy = b_iter.next(); } else if x < y { ox = a_iter.next(); } } if beautiful_count == a.len() { beautiful_count - 1 } else { beautiful_count + 1 } } 

But we don’t need to sort the array if we use the frequency table, so we can pass an immutable slice instead of an owned Vec.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21  pub fn beautiful_pairs(a: &[usize], b: &[usize]) -> usize { let mut beautiful_count = 0; let mut freq_table = [0; 1001]; for xb in b { freq_table[*xb] += 1; } for xa in a { if freq_table[*xa] > 0 { freq_table[*xa] -= 1; beautiful_count += 1; } } if beautiful_count == a.len() { beautiful_count - 1 } else { beautiful_count + 1 } } 

The unsafe keyword here is needed to optimize the algorithm so the access to an element of the frequency table is unchecked. We can safely do this until all items in a and b have a value < 1001.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  fn beautiful_pairs(a: &[usize], b: &[usize]) -> usize { let mut beautiful_count = 0; let mut freq_table = [0; 1001]; unsafe { for xb in b { *freq_table.get_unchecked_mut(*xb) += 1; } for xa in a { if *freq_table.get_unchecked(*xa) > 0 { *freq_table.get_unchecked_mut(*xa) -= 1; beautiful_count += 1; } } } if beautiful_count == a.len() { beautiful_count - 1 } else { beautiful_count + 1 } } 

The resulting asm can be compared here

We've worked on this challenge in these gyms: modena  padua