Skip to content

Commit

Permalink
feat(C++): Add 1963
Browse files Browse the repository at this point in the history
TIL/Realized that a single swap can match two parentheses.
Good to know. Nice to revisit.
  • Loading branch information
euchangxian committed Oct 8, 2024
1 parent 0af2620 commit db9644b
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
90 changes: 90 additions & 0 deletions C++/1963-MinimumNumberOfSwapsToMakeTheStringsBalanced/Solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include <algorithm>
#include <array>
#include <bitset>
#include <climits>
#include <cstdint>
#include <functional>
#include <iostream>
#include <queue>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;
class Solution {
public:
int minSwaps(string s) {
// Stack? can keep track of unmatched pairs? Does not work with the case:
// "]]][[[", where the stack would result in 3, but the answer is 2 swaps.
// The optimal way is this sequence of swaps:
// "[]][[]" => Swap index 0 ']' with index 5 '['
// "[][][]" => Swap index 2 ']' with index 3 '['
// GREEDY... greedily swap the first closing bracket ']' with the last
// opening bracket '['.
//
// How to prove that the greedy choice is optimal?
// Two cases:
// 1. The last opening bracket '[' is unpaired. By swapping the first
// closing bracket ']' with the last opening bracket '[', we reduce the
// number of unpaired brackets by AT LEAST 1. This is because either the
// pattern "[]...[]" or "[...]" occurs.
//
// 2. The last opening bracket '[' is paired. By swapping, the number of
// unpaired brackets will either stay the same or reduce by 1.
// E.g. Initially, "]...[]". By swapping, we get either "[]...[]" or
// "[...[]" (stay the same).
//
// Correction: The above is not really a proof. Just an enumeration of
// cases.
// More precisely, given two unmatched pairs, we can always swap parentheses
// such that only one swap is needed to match both pairs.
// The question is how to prove this? IDK.
//
// Given this proof, we can easily arrive at the fact that the minimum
// number of swaps required is math.ceil(numUnmatched / 2).
//
// Proof below by Claude, proof-read by ME
// Proof of optimality:
// 1. Lemma: In any valid bracket string, the number of unmatched '['
// equals the number of unmatched ']'.
// Proof: Total '[' must equal total ']' for validity. Any mismatch
// in one direction is balanced by a mismatch in the other.
// 2. Definition: Unmatched '[' is a "right mismatch",
// unmatched ']' is a "left mismatch".
// 3. Theorem: Any two mismatches (one left, one right) can be resolved
// with a single swap.
// Proof:
// - Consider leftmost left mismatch ']' and rightmost right mismatch
// '['.
// - Swapping these reduces total mismatches by 2:
// - ']' now matches with some '[' to its left.
// - '[' now matches with some ']' to its right.
// - True regardless of what's between them (from Lemma 1).
// 4. Corollary: Minimum swaps needed = ceil(numUnmatched / 2).
// Proof:
// - numUnmatched is even (equal left and right mismatches, Lemma 1).
// - We can resolve any two mismatches with one swap (Theorem 3).
// - Therefore, we need numUnmatched / 2 swaps.
// - ceil() handles the case where numUnmatched is 0.

int numUnmatched = 0;
int openingBrackets = 0; // like a Stack
for (const char c : s) {
if (c == '[') {
++openingBrackets;
continue;
}

// c == ']'
if (openingBrackets > 0) {
--openingBrackets;
} else {
++numUnmatched;
}
}

return (numUnmatched + 1) / 2;
}
};
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ Now solving in C++. Like it.
| 1937 | MaximumNumberOfPointsWithCost | [![C++](assets/c++.svg)](C++/1937-MaximumNumberOfPointsWithCost/Solution.cpp) |
| 1945 | SumOfDigitsOfStringAfterConvert | [![C++](assets/c++.svg)](C++/1945-SumOfDigitsOfStringAfterConvert/Solution.cpp) |
| 1953 | MaximumNumberOfWeeksForWhichYouCanWork | [![C++](assets/c++.svg)](C++/1953-MaximumNumberOfWeeksForWhichYouCanWork/Solution.cpp) |
| 1963 | MinimumNumberOfSwapsToMakeTheStringsBalanced | [![C++](assets/c++.svg)](C++/1963-MinimumNumberOfSwapsToMakeTheStringsBalanced/Solution.cpp) |
| 1979 | FindGreatestCommonDivisorOfArray | [![C++](assets/c++.svg)](C++/1979-FindGreatestCommonDivisorOfArray/Solution.cpp) |
| 2000 | ReversePrefixOfWord | [![Go](assets/go.svg)](Go/2000-ReversePrefixOfWord/Solution.go) |
| 2022 | Convert1DArrayTo2DArray | [![C++](assets/c++.svg)](C++/2022-Convert1DArrayTo2DArray/Solution.cpp) |
Expand Down

0 comments on commit db9644b

Please sign in to comment.