Minimum Cost to Make All Characters Equal
MedPrompt
You are given a 0-indexed binary string s of length n on which you can apply two types of operations:
- Choose an index
iand invert all characters from index0to indexi(both inclusive), with a cost ofi + 1 - Choose an index
iand invert all characters from indexito indexn - 1(both inclusive), with a cost ofn - i
Return the minimum cost to make all characters of the string equal.
Invert a character means if its value is '0' it becomes '1' and vice-versa.
Example 1:
i = 2Example 2:
Input: s = "010101"
Output: 9
Explanation: Apply the first operation with i = 2 to obtain s = "101101" for a cost of 3.
Apply the first operation with i = 1 to obtain s = "011101" for a cost of 2.
Apply the first operation with i = 0 to obtain s = "111101" for a cost of 1.
Apply the second operation with i = 4 to obtain s = "111110" for a cost of 2.
Apply the second operation with i = 5 to obtain s = "111111" for a cost of 1.
The total cost to make all characters equal is 9. It can be shown that 9 is the minimum cost to make all characters equal.
Constraints:
1 <= s.length == n <= 105s[i]is either'0'or'1'
Approaches
3 approaches with complexity analysis and trade-offs.
This is a space-optimized version of the dynamic programming approach. Since the calculation for the current step only depends on the result from the previous step, we don't need to store the entire DP array. We can use a single variable to accumulate the total cost as we iterate through the string once.
Algorithm
- Initialize
totalCost = 0. - Iterate through the string from the second character (
i = 1ton-1). - At each position
i, compares.charAt(i)withs.charAt(i-1). - If they are different, it means there is a mismatch that must be resolved.
- The cost to resolve this mismatch is the minimum of flipping the prefix
s[0...i-1](costi) or flipping the suffixs[i...n-1](costn-i). - Add
min(i, n - i)tototalCost. - After the loop finishes,
totalCostwill hold the total minimum cost.
Walkthrough
This approach is built on the same fundamental insight as the DP solution: the total minimum cost is the sum of the minimum costs to resolve each individual mismatch between adjacent characters. A mismatch occurs at index i-1 if s[i-1] != s[i]. To resolve this, we must apply an operation that flips one character but not the other. The two options are:
- A prefix flip up to
i-1, with cost(i-1) + 1 = i. - A suffix flip from
i, with costn - i.
The minimum cost to resolve this specific mismatch is min(i, n - i). Since these choices are independent for each mismatch, we can simply iterate through the string, find all mismatches, calculate the minimum cost for each, and sum them up. We use a single variable, totalCost, to keep a running sum, which optimizes space.
class Solution { public long minimumCost(String s) { int n = s.length(); long totalCost = 0; // Iterate through the string to find adjacent characters that are different. for (int i = 1; i < n; i++) { if (s.charAt(i) != s.charAt(i-1)) { // A mismatch at index i-1 needs to be fixed. // We can either flip the prefix ending at i-1 (cost i) // or flip the suffix starting at i (cost n-i). // We choose the cheaper of the two operations. totalCost += Math.min(i, n - i); } } return totalCost; }}Complexity
Time
O(n), as we iterate through the string exactly once.
Space
O(1), as we only use a few variables to store the cost and loop index, regardless of the input size.
Trade-offs
Pros
Optimal time complexity of O(n).
Optimal space complexity of O(1).
Simple, intuitive, and easy to implement.
Cons
There are no significant cons to this approach as it is optimal.
This approach uses dynamic programming based on a key insight: the total cost is the sum of costs to fix each "change point" where adjacent characters are different. An operation either fixes a single change point or doesn't affect any. This allows us to build up the solution iteratively.
Algorithm
- The key insight is that the total cost is the sum of costs to fix each position
iwheres[i] != s[i+1]. - For each such "mismatch", we must perform an operation that flips one of the characters but not the other. The two options are a prefix flip up to
i(costi+1) or a suffix flip fromi+1(costn-(i+1)). - We can use dynamic programming. Let
dp[i]be the minimum cost to make the prefixs[0...i]uniform. - The recurrence relation is:
dp[i] = dp[i-1]ifs[i] == s[i-1], anddp[i] = dp[i-1] + min(i, n-i)ifs[i] != s[i-1]. - The base case is
dp[0] = 0. - The final answer is
dp[n-1].
Walkthrough
The core idea is that to make the string uniform, we must eliminate all indices i where s[i] != s[i+1]. To resolve such a mismatch, we must apply an operation that splits i and i+1. The two choices are a prefix flip up to i (cost i+1) or a suffix flip from i+1 (cost n-(i+1)). Since fixing one mismatch doesn't create or resolve another, we can consider each mismatch independently and choose the cheaper operation for each.
We can define a DP state dp[i] as the minimum cost to resolve all mismatches in the prefix s[0...i]. We build the solution from left to right.
class Solution { public long minimumCost(String s) { int n = s.length(); if (n <= 1) { return 0; } // dp[i] stores the minimum cost to make the prefix s[0...i] uniform. long[] dp = new long[n]; dp[0] = 0; // A single character prefix is always uniform. for (int i = 1; i < n; i++) { // Start with the cost to make the previous prefix uniform. dp[i] = dp[i-1]; // If there is a mismatch between s[i] and s[i-1], we must fix it. if (s.charAt(i) != s.charAt(i-1)) { // To fix the mismatch at index i-1, we need an operation that // splits i-1 and i. // Option 1: Prefix flip up to i-1. Cost = (i-1) + 1 = i. // Option 2: Suffix flip from i. Cost = n - i. long costToFix = Math.min(i, n - i); dp[i] += costToFix; } } return dp[n - 1]; }}Complexity
Time
O(n), due to the single loop through the string of length n.
Space
O(n) for the DP array.
Trade-offs
Pros
Much more efficient than brute-force.
Correctly identifies the independent nature of solving each mismatch.
Provides a clear, structured way to think about the problem.
Cons
Uses O(n) extra space for the DP array, which is not strictly necessary.
This approach models the problem as finding the shortest path in a state-space graph. Each possible binary string is a node, and an operation is a weighted edge connecting two nodes. We can use a graph search algorithm like Dijkstra's to find the minimum cost path from the initial string s to any uniform string (all '0's or all '1's).
Algorithm
- Model the problem as a shortest path problem on a graph.
- Each unique binary string of length
nis a node in the graph. - An edge exists between two string nodes if one can be transformed into the other by a single operation. The weight of the edge is the cost of that operation.
- Use Dijkstra's algorithm to find the shortest path from the initial string
sto any uniform string (all '0's or all '1's). - Use a priority queue to explore states with the lowest cost first and a hash map to store minimum costs to visited states to avoid cycles.
Walkthrough
The state in our search is the current string, and the cost is the accumulated cost of operations. We use a priority queue to always explore the state with the minimum accumulated cost first, which is the core of Dijkstra's algorithm. A map or hash table is used to keep track of the minimum cost found so far for each string to avoid cycles and redundant computations.
The algorithm starts with the initial string s and a cost of 0. In each step, it extracts the string with the lowest cost from the priority queue. If this string is uniform, we have found the minimum cost. Otherwise, it generates all possible next strings by applying each of the 2n operations (prefix flips for i=0..n-1 and suffix flips for i=0..n-1). For each new string, if a cheaper path to it is found, its cost is updated, and it's added to the priority queue.
// This approach is purely theoretical for the given constraints and would time out.// A full implementation is impractical due to the massive state space.class Solution { // Conceptual structure of a brute-force Dijkstra's approach. public long minimumCost(String s) { int n = s.length(); PriorityQueue<Pair<Long, String>> pq = new PriorityQueue<>(Comparator.comparingLong(Pair::getKey)); Map<String, Long> minCosts = new HashMap<>(); pq.add(new Pair<>(0L, s)); minCosts.put(s, 0L); while (!pq.isEmpty()) { Pair<Long, String> current = pq.poll(); long cost = current.getKey(); String currentS = current.getValue(); if (isUniform(currentS)) { return cost; } if (cost > minCosts.get(currentS)) { continue; } // Explore all 2n possible operations for (int i = 0; i < n; i++) { // ... logic to apply prefix and suffix flips ... // ... update pq and minCosts if a cheaper path is found ... } } return -1; // Should not be reached } private boolean isUniform(String s) { if (s.length() <= 1) return true; char first = s.charAt(0); for (int i = 1; i < s.length(); i++) { if (s.charAt(i) != first) return false; } return true; }}Complexity
Time
O(n^2 * 2^n), The state space is V = 2^n. From each state, there are E = O(n) transitions. Each transition involves O(n) work for string manipulation. Dijkstra's would be O(E log V) or O(E+V log V) depending on implementation, leading to a complexity that is exponential in n.
Space
O(n * 2^n), to store the minimum costs for all possible 2^n strings of length n.
Trade-offs
Pros
Guaranteed to find the optimal solution if it could run to completion.
It's a direct application of a well-known algorithm.
Cons
Extremely inefficient in both time and space.
The number of states (2^n) makes it infeasible for the given constraints (n <= 10^5).
String manipulation and storage are very expensive.
Solutions
Solution
class Solution {public long minimumCost(String s) { long ans = 0; int n = s.length(); for (int i = 1; i < n; ++i) { if (s.charAt(i) != s.charAt(i - 1)) { ans += Math.min(i, n - i); } } return ans; }}class Solution {public: long long minimumCost(string s) { long long ans = 0; int n = s.size(); for (int i = 1; i < n; ++i) { if (s[i] != s[i - 1]) { ans += min(i, n - i); } } return ans; }};class Solution: def minimumCost(self, s: str) -> int: ans, n = 0, len(s) for i in range(1, n): if s[i] != s[i - 1]: ans += min(i, n - i) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.