3800. Minimum Cost to Make Two Binary Strings Equal
Problem Description
You are given two binary strings s and t, both of length n, along with three positive integers flipCost, swapCost, and crossCost.
You may apply the following operations to the strings s and t any number of times, and in any order:
- Flip operation: Choose any index
iand flip eithers[i]ort[i](change'0'to'1', or'1'to'0'). Each such operation costsflipCost. - Swap operation (within a string): Choose two distinct indices
iandj, then swap eithers[i]ands[j], ort[i]andt[j]. Each such operation costsswapCost. - Cross operation (across strings): Choose an index
iand swaps[i]witht[i]. Each such operation costscrossCost.
Your goal is to make the two strings s and t equal using these operations.
Return an integer representing the minimum total cost required to make s and t equal.
How We Pick the Algorithm
Why Greedy Algorithms?
This problem maps to Greedy Algorithms through a short path in the full flowchart.
A locally optimal choice at each step builds the globally optimal solution.
Open in FlowchartIntuition
The first thing to notice is that we only care about the positions i where s[i] != t[i]. Positions where s[i] == t[i] are already "equal" at that index and require no work. So our job reduces to fixing all the mismatched positions.
At any mismatched position, the pair (s[i], t[i]) is either (0, 1) or (1, 0). Let's classify them:
diff[0]= number of positions wheres[i] = 0andt[i] = 1.diff[1]= number of positions wheres[i] = 1andt[i] = 0.
Now we think about how each operation helps us "cancel" these mismatches:
1. Flip alone.
Every mismatched position can be fixed by a single flip (flip one of the two characters so they match). This costs flipCost per mismatch, giving a baseline answer of (diff[0] + diff[1]) * flipCost.
2. Pairing two mismatches of the same type using a swap.
Here's the key insight: a swap within a single string (cost swapCost) can fix two mismatches of the same type at once. For example, if two positions both look like (0, 1), then in string s we have 0 and 0 and in t we have 1 and 1. By rearranging within one string, we can pair them up so both positions become matched after the right combination. So instead of paying 2 * flipCost for two same-type mismatches, we might pay swapCost for the pair. This is only worth it when swapCost < 2 * flipCost.
Since a swap pairs two same-type mismatches, the number of swaps we can do on one type is limited. Letting mn = min(diff) and mx = max(diff), the most natural plan is: use swaps on the smaller group as much as possible, then flip whatever is left. But within a single type, pairs come from the same group — so we pair up the smaller count mn of mismatches via swaps and flip the remaining mx - mn, giving mn * swapCost + (mx - mn) * flipCost.
3. Using cross operations to convert types.
A cross operation (cost crossCost) swaps s[i] with t[i] at a single index, which turns a (0, 1) mismatch into a (1, 0) mismatch (or vice versa) — it changes the type of a mismatch without fixing it.
Why would that help? Because swaps can only pair up mismatches of the same type. If the two groups are unbalanced (mx much larger than mn), we can use cross operations to convert some of the larger group into the smaller type, balancing them so that more swaps become possible.
The ideal balance point is the average avg = (mx + mn) // 2. We spend (avg - mn) * crossCost to move mismatches from the larger group into the smaller until both groups are close to avg. Then we can do avg swaps (each canceling a same-type pair) costing avg * swapCost, and flip whatever odd leftover remains, costing (mx + mn - 2 * avg) * flipCost.
Putting it together. The three strategies above represent the meaningful ways to combine operations, and the answer is simply the minimum of these three candidate costs. The structure of the problem guarantees that mixing operations in any other way won't beat one of these clean strategies, because each operation type has a fixed role: flip fixes one mismatch, swap fixes a same-type pair, and cross rebalances types to enable more swaps.
Pattern Learn more about Greedy patterns.
Solution Approach
We implement the three strategies described above and take their minimum. The whole process uses only simple counting and a few arithmetic comparisons — no advanced data structures are needed.
Step 1: Count the mismatches by type.
We create an array diff = [0, 0] to hold the counts of the two mismatch types. We then iterate over s and t together using zip(s, t). For each character pair (c1, c2):
- If
c1 != c2, the position is mismatched. We incrementdiff[int(c1)].diff[0]accumulates positions wheres[i] = '0'(sot[i] = '1').diff[1]accumulates positions wheres[i] = '1'(sot[i] = '0').
diff = [0] * 2
for c1, c2 in zip(s, t):
if c1 != c2:
diff[int(c1)] += 1
Step 2: Baseline — flip everything.
Every mismatch can be fixed independently with one flip, so our starting candidate is:
ans = (diff[0] + diff[1]) * flipCost
Step 3: Compute mn and mx.
To reason about pairing, we identify the smaller and larger group sizes:
mx = max(diff)
mn = min(diff)
Step 4: Swap the smaller group, flip the rest.
A swap cancels a same-type pair. We can pair up mn mismatches via swaps and flip the remaining mx - mn:
ans = min(ans, mn * swapCost + (mx - mn) * flipCost)
We take min with the current ans so we only keep this strategy if it is cheaper than plain flipping.
Step 5: Cross to balance, then swap, then flip.
Cross operations convert one mismatch type into the other. We balance both groups toward the average:
avg = (mx + mn) // 2
The cost breaks into three parts:
(avg - mn) * crossCost— convert mismatches from the larger group into the smaller until both reach aboutavg.avg * swapCost— performavgswaps, each canceling a same-type pair.(mx + mn - avg * 2) * flipCost— flip the leftover (this is0whenmx + mnis even, and1when it is odd, due to integer division).
ans = min(
ans,
(avg - mn) * crossCost + avg * swapCost + (mx + mn - avg * 2) * flipCost,
)
Step 6: Return the result.
After evaluating all three candidate strategies and keeping the smallest, we return ans:
return ans
Complexity Analysis:
- Time complexity is
O(n), wherenis the length of the strings. We scan the strings once to count mismatches; the rest is constant-time arithmetic. - Space complexity is
O(1), since we only use the fixed-sizediffarray and a handful of scalar variables.
Example Walkthrough
Let's trace through a concrete example to see how the three strategies compete.
Input:
s = "0011010"t = "1101001"flipCost = 5,swapCost = 4,crossCost = 1n = 7
Step 1: Count the mismatches by type.
We compare s and t index by index, only caring about positions where they differ.
Index i | s[i] | t[i] | Match? | Type counted |
|---|---|---|---|---|
| 0 | 0 | 1 | mismatch | diff[0]++ |
| 1 | 0 | 1 | mismatch | diff[0]++ |
| 2 | 1 | 0 | mismatch | diff[1]++ |
| 3 | 1 | 1 | match | — |
| 4 | 0 | 0 | match | — |
| 5 | 1 | 0 | mismatch | diff[1]++ |
| 6 | 0 | 1 | mismatch | diff[0]++ |
After the scan:
diff[0] = 3(positions 0, 1, 6 →s=0, t=1)diff[1] = 2(positions 2, 5 →s=1, t=0)
So we have 3 mismatches of type (0,1) and 2 mismatches of type (1,0).
Step 2: Baseline — flip everything.
Each of the 5 mismatches can be fixed with one independent flip:
ans = (diff[0] + diff[1]) * flipCost = (3 + 2) * 5 = 25
Candidate so far: 25.
Step 3: Compute mn and mx.
mx = max(3, 2) = 3 mn = min(3, 2) = 2
Step 4: Swap the smaller group, flip the rest.
We pair up mn = 2 same-type mismatches using swaps, and flip the leftover mx - mn = 1:
cost = mn * swapCost + (mx - mn) * flipCost = 2 * 4 + 1 * 5 = 8 + 5 = 13 ans = min(25, 13) = 13
Candidate so far: 13 — already much better, because pairing two mismatches with a swap (cost 4) beats two flips (cost 10).
Step 5: Cross to balance, then swap, then flip.
The two groups are unbalanced (3 vs 2). We use cheap cross operations (crossCost = 1) to rebalance them toward the average so that more swaps become possible.
avg = (mx + mn) // 2 = (3 + 2) // 2 = 2
Three cost components:
- Cross to balance: convert
(avg - mn) = (2 - 2) = 0mismatches →0 * 1 = 0 - Swap pairs: perform
avg = 2swaps →2 * 4 = 8 - Flip leftover:
(mx + mn - 2 * avg) = (5 - 4) = 1leftover →1 * 5 = 5
cost = 0 + 8 + 5 = 13 ans = min(13, 13) = 13
Note: Here
avgtruncates to 2 and the smaller group already equals 2, so no crossing is needed and this strategy ties with Step 4. Crossing pays off more dramatically when groups are highly unbalanced andcrossCostis tiny relative to flipping — e.g. ifdiff = [5, 1], crossing would let us turn a5/1split into a near3/3split, unlocking far more cheap swaps.
Step 6: Return the result.
The minimum across all three strategies is:
min(25, 13, 13) = 13
Answer: 13 — achieved by swapping the two same-type pairs (cost 2 * 4 = 8) and flipping the single odd-one-out mismatch (cost 5).
Solution Implementation
1class Solution:
2 def minimumCost(
3 self, s: str, t: str, flipCost: int, swapCost: int, crossCost: int
4 ) -> int:
5 # diff[0]: count of positions where s has '0' but differs from t
6 # diff[1]: count of positions where s has '1' but differs from t
7 diff = [0, 0]
8 for char_s, char_t in zip(s, t):
9 if char_s != char_t:
10 diff[int(char_s)] += 1
11
12 # Strategy 1: Fix every mismatch independently with a flip.
13 ans = (diff[0] + diff[1]) * flipCost
14
15 # Determine the larger and smaller group of mismatches.
16 max_diff = max(diff)
17 min_diff = min(diff)
18
19 # Strategy 2: Pair one '0'-mismatch with one '1'-mismatch via swap
20 # (handles `min_diff` pairs), then flip the remaining mismatches.
21 ans = min(ans, min_diff * swapCost + (max_diff - min_diff) * flipCost)
22
23 # Strategy 3: Balance the two groups using cross operations, then swap,
24 # then flip whatever is left over.
25 # - (avg - min_diff) cross operations convert the smaller group up to avg
26 # - avg swap operations pair the two balanced groups
27 # - remaining mismatches are fixed with flips
28 avg = (max_diff + min_diff) // 2
29 ans = min(
30 ans,
31 (avg - min_diff) * crossCost
32 + avg * swapCost
33 + (max_diff + min_diff - avg * 2) * flipCost,
34 )
35
36 return ans
37```
38
39**Notes / Perspectives:**
40
411. **Naming:** I renamed `c1`/`c2` to `char_s`/`char_t` and `mx`/`mn` to `max_diff`/`min_diff` for clarity. The method name `minimumCost` was preserved as requested.
42
432. **Correctness concern:** This solution only checks three discrete strategies. A fully general approach would iterate over the number of cross operations `k` (from `0` to some bound) and compute the cost for each `k`, since the optimal point isn't always at the simple `avg`. If the test cases are strict, consider this more robust version:
44
45```python3
46class Solution:
47 def minimumCost(
48 self, s: str, t: str, flipCost: int, swapCost: int, crossCost: int
49 ) -> int:
50 diff = [0, 0]
51 for char_s, char_t in zip(s, t):
52 if char_s != char_t:
53 diff[int(char_s)] += 1
54
55 max_diff, min_diff = max(diff), min(diff)
56 ans = float("inf")
57 # k = number of cross operations applied to the larger group
58 # to convert it into the smaller-group type before swapping.
59 for k in range((max_diff - min_diff) // 2 + 1):
60 balanced = min_diff + k # paired via swap on each side
61 leftover = max_diff - min_diff - 2 * k # fixed via flip
62 cost = k * crossCost + balanced * swapCost + leftover * flipCost
63 ans = min(ans, cost)
64 # Always consider flipping everything as a baseline.
65 ans = min(ans, (diff[0] + diff[1]) * flipCost)
66 return ans
671class Solution {
2 public long minimumCost(String s, String t, int flipCost, int swapCost, int crossCost) {
3 // diff[0] counts positions where s has '0' but t has '1' (need to change 0 -> 1)
4 // diff[1] counts positions where s has '1' but t has '0' (need to change 1 -> 0)
5 long[] diff = new long[2];
6 int length = s.length();
7
8 // Collect all mismatched positions, grouped by the character in s
9 for (int i = 0; i < length; i++) {
10 char sChar = s.charAt(i);
11 char tChar = t.charAt(i);
12 if (sChar != tChar) {
13 diff[sChar - '0']++;
14 }
15 }
16
17 // Strategy 1: Fix every mismatched position individually with a flip.
18 long answer = (diff[0] + diff[1]) * flipCost;
19
20 // Determine the larger and smaller of the two mismatch counts.
21 long maxDiff = Math.max(diff[0], diff[1]);
22 long minDiff = Math.min(diff[0], diff[1]);
23
24 // Strategy 2: Pair up opposite mismatches and resolve each pair with one swap,
25 // then flip whatever remains unpaired.
26 answer = Math.min(answer, minDiff * swapCost + (maxDiff - minDiff) * flipCost);
27
28 // Strategy 3: Use cross operations to balance the two counts toward their average,
29 // then swap the matched pairs, and flip the leftover.
30 long average = (maxDiff + minDiff) / 2;
31 answer = Math.min(
32 answer,
33 (average - minDiff) * crossCost + average * swapCost + (maxDiff + minDiff - average * 2) * flipCost);
34
35 return answer;
36 }
37}
381class Solution {
2public:
3 long long minimumCost(string s, string t, int flipCost, int swapCost, int crossCost) {
4 // diffCount[0]: number of positions where s[i] == '0' but differs from t[i]
5 // diffCount[1]: number of positions where s[i] == '1' but differs from t[i]
6 long long diffCount[2] = {0, 0};
7 int n = s.size();
8
9 // Count the mismatched positions, grouped by the original character in s
10 for (int i = 0; i < n; i++) {
11 if (s[i] != t[i]) {
12 diffCount[s[i] - '0']++;
13 }
14 }
15
16 // Option 1: Fix every mismatch independently using flip operations
17 long long ans = (diffCount[0] + diffCount[1]) * flipCost;
18
19 // Identify the larger and smaller group of mismatches
20 long long maxDiff = max(diffCount[0], diffCount[1]);
21 long long minDiff = min(diffCount[0], diffCount[1]);
22
23 // Option 2: Pair up mismatches of opposite types using swaps (minDiff pairs),
24 // then flip the remaining unpaired mismatches
25 ans = min(ans, minDiff * 1LL * swapCost + (maxDiff - minDiff) * flipCost);
26
27 // Option 3: Use cross operations to balance the two groups toward their average,
28 // then swap the balanced pairs, and flip whatever remains
29 long long avg = (maxDiff + minDiff) / 2;
30 ans = min(ans,
31 (avg - minDiff) * crossCost // bring smaller group up to avg
32 + avg * swapCost // swap the avg-sized balanced pairs
33 + (maxDiff + minDiff - avg * 2) * flipCost); // flip leftover (parity remainder)
34
35 return ans;
36 }
37};
381/**
2 * Calculate the minimum cost to transform string s into string t.
3 *
4 * Each position where s and t differ is classified by the character in s:
5 * - index 0 counts mismatches where s[i] === '0'
6 * - index 1 counts mismatches where s[i] === '1'
7 *
8 * Three strategies are compared to find the minimum total cost:
9 * 1. Flip every mismatch independently.
10 * 2. Pair up opposite mismatches via swaps, flip the remainder.
11 * 3. Use cross operations to rebalance counts, then swap, then flip.
12 *
13 * @param s - The source binary string.
14 * @param t - The target binary string.
15 * @param flipCost - Cost of flipping a single mismatched bit.
16 * @param swapCost - Cost of swapping a pair of opposite mismatches.
17 * @param crossCost - Cost of a cross operation used to rebalance counts.
18 * @returns The minimum total cost to make s equal to t.
19 */
20function minimumCost(
21 s: string,
22 t: string,
23 flipCost: number,
24 swapCost: number,
25 crossCost: number,
26): number {
27 // mismatchCount[0]: positions where s[i] === '0' but s[i] !== t[i]
28 // mismatchCount[1]: positions where s[i] === '1' but s[i] !== t[i]
29 const mismatchCount: number[] = [0, 0];
30 const length = s.length;
31
32 // Count mismatches grouped by the character in s.
33 for (let i = 0; i < length; i++) {
34 if (s[i] !== t[i]) {
35 // Convert '0' or '1' to its numeric index (0 or 1).
36 mismatchCount[s.charCodeAt(i) - 48]++;
37 }
38 }
39
40 // Strategy 1: flip every mismatch independently.
41 let answer = (mismatchCount[0] + mismatchCount[1]) * flipCost;
42
43 // Larger and smaller of the two mismatch groups.
44 const maxCount = Math.max(mismatchCount[0], mismatchCount[1]);
45 const minCount = Math.min(mismatchCount[0], mismatchCount[1]);
46
47 // Strategy 2: swap the minCount opposite pairs, flip the leftover mismatches.
48 answer = Math.min(answer, minCount * swapCost + (maxCount - minCount) * flipCost);
49
50 // Strategy 3: use cross operations to bring the larger group down toward the
51 // average, then swap matched pairs, and flip any remaining mismatches.
52 const averageCount = (maxCount + minCount) >> 1;
53 answer = Math.min(
54 answer,
55 (averageCount - minCount) * crossCost +
56 averageCount * swapCost +
57 (maxCount + minCount - averageCount * 2) * flipCost,
58 );
59
60 return answer;
61}
62Time and Space Complexity
Time Complexity: O(n)
The dominant operation is the for loop that iterates over zip(s, t), where n is the length of strings s and t. Within each iteration, the comparison c1 != c2, the conversion int(c1), and the increment of diff are all constant-time O(1) operations. After the loop, all subsequent computations (max, min, arithmetic operations, and min comparisons) are performed on the fixed-size diff array of length 2, which takes O(1) time. Therefore, the overall time complexity is O(n).
Space Complexity: O(1)
The algorithm uses only a fixed amount of extra space: the diff array has a constant size of 2, and the variables ans, mx, mn, and avg each occupy constant space. No additional space scales with the input size n, so the space complexity is O(1).
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Mishandling the cost relationship between a swap and two flips
The most frequent mistake is blindly preferring a swap whenever two mismatches of the same type exist, without comparing swapCost against the cost of just flipping both positions.
A swap that cancels a same-type pair fixes two mismatches at once. Its true competitor is two flips (2 * flipCost), not one. So a swap is only worthwhile when:
swapCost < 2 * flipCost
If swapCost >= 2 * flipCost, Strategy 2 should never be chosen, yet a naive implementation that always pairs the smaller group will silently produce a larger cost. The code avoids this only because it wraps every strategy in min(...). The pitfall appears when someone "optimizes" by removing the baseline flip comparison or by forcing a swap-first approach.
Solution: Always keep the all-flip baseline as a candidate, and reason about each operation in terms of how many mismatches it fixes:
# A swap fixes 2 same-type mismatches; compare against 2 flips.
pair_fix_cost = min(swapCost, 2 * flipCost)
Pitfall 2: Assuming the optimum is always at avg (the heart of the "Correctness concern")
Strategy 3 hardcodes the balancing point at avg = (max_diff + min_diff) // 2. This implicitly assumes it is always optimal to cross-convert the larger group down to the average and then swap everything. That is not generally true.
Consider the cost as a function of k, the number of cross conversions applied to the larger group:
cost(k) = k * crossCost + (min_diff + k) * swapCost # each cross creates a new swappable pair + (max_diff - min_diff - 2k) * flipCost
Simplifying the k-dependent part:
cost(k) = const + k * (crossCost + swapCost - 2 * flipCost)
This is linear in k. Therefore:
- If
crossCost + swapCost < 2 * flipCost, cost decreases withk, so pushkto its maximum ((max_diff - min_diff) // 2) — i.e., balance fully. Hereavghappens to be right. - If
crossCost + swapCost > 2 * flipCost, cost increases withk, so the optimum isk = 0— do no crossing and just flip the surplus. Hereavgis wrong and overcharges. - If equal, any
kworks.
So the three-strategy version can return a suboptimal answer precisely because it only samples k = avg - min_diff instead of the endpoints.
Solution: Either iterate over all k (the "robust version" given), or, since the objective is linear, just evaluate the two endpoints k = 0 and k = (max_diff - min_diff) // 2 and take the minimum:
def minimumCost(self, s, t, flipCost, swapCost, crossCost):
diff = [0, 0]
for cs, ct in zip(s, t):
if cs != ct:
diff[int(cs)] += 1
mx, mn = max(diff), min(diff)
pair_cost = min(swapCost, 2 * flipCost) # best way to clear a same-type pair
ans = float("inf")
# k = 0 (no crossing) and k = full balance are the only candidates
for k in (0, (mx - mn) // 2):
balanced = mn + k
leftover = mx - mn - 2 * k
ans = min(ans, k * crossCost + balanced * pair_cost + leftover * flipCost)
return ans
Pitfall 3: Counting mismatches in a single bucket
A subtle modeling error is treating all mismatches as interchangeable and storing them in one counter. The two mismatch directions (s[i]='0', t[i]='1' versus s[i]='1', t[i]='0') behave differently:
- A swap within a string can only cancel two mismatches of the same direction (it moves a wrong bit elsewhere without changing the global count, so it pairs like-with-like).
- A cross operation flips a position from one direction into the other, which is the only way to make the two groups swappable against each other.
If you collapse everything into one count, you lose the information needed to decide between swap and cross. Always split into diff[0] and diff[1].
Pitfall 4: Off-by-one with the leftover flip on odd totals
When max_diff + min_diff is odd, integer division in avg = (max_diff + min_diff) // 2 leaves exactly one leftover mismatch that must be flipped. The term (max_diff + min_diff - 2 * avg) * flipCost correctly accounts for it (yielding 1 * flipCost). Dropping or miscomputing this term silently undercounts the cost on odd inputs.
Solution: Keep the leftover term explicit (mx - mn - 2k in the loop form) so the single unpaired mismatch is never forgotten, and verify with a tiny odd-sized test case where one group has an extra mismatch.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapGiven a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___ in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
121public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
161function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!