Change Minimum Characters to Satisfy One of Three Conditions
MedPrompt
You are given two strings a and b that consist of lowercase letters. In one operation, you can change any character in a or b to any lowercase letter.
Your goal is to satisfy one of the following three conditions:
- Every letter in
ais strictly less than every letter inbin the alphabet. - Every letter in
bis strictly less than every letter inain the alphabet. - Both
aandbconsist of only one distinct letter.
Return the minimum number of operations needed to achieve your goal.
Example 1:
Input: a = "aba", b = "caa"
Output: 2
Explanation: Consider the best way to make each condition true:
1) Change b to "ccc" in 2 operations, then every letter in a is less than every letter in b.
2) Change a to "bbb" and b to "aaa" in 3 operations, then every letter in b is less than every letter in a.
3) Change a to "aaa" and b to "aaa" in 2 operations, then a and b consist of one distinct letter.
The best way was done in 2 operations (either condition 1 or condition 3).Example 2:
Input: a = "dabadd", b = "cda"
Output: 3
Explanation: The best way is to make condition 1 true by changing b to "eee".
Constraints:
1 <= a.length, b.length <= 105aandbconsist only of lowercase letters.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem statement into code. It calculates the minimum operations required for each of the three conditions separately and then returns the overall minimum. For each condition, it iterates through all 26 possible lowercase letters, treating them as either a target character (for condition 3) or a boundary (for conditions 1 and 2). The cost for each potential character is found by iterating through the input strings a and b.
Algorithm
- Initialize
min_opsto a value larger than any possible result, such asa.length() + b.length(). - Calculate cost for Condition 1 (all
a< allb):- Loop through each character
cfrom 'b' to 'z'. Thiscacts as the boundary. - For each
c, calculate the operationsopsneeded. This is the sum of characters inathat are>= cand characters inbthat are< c. - Update
min_ops = min(min_ops, ops).
- Loop through each character
- Calculate cost for Condition 2 (all
b< alla):- This is symmetric to condition 1. Loop through each character
cfrom 'b' to 'z'. - For each
c, calculateopsby counting characters inbthat are>= cand characters inathat are< c. - Update
min_ops = min(min_ops, ops).
- This is symmetric to condition 1. Loop through each character
- Calculate cost for Condition 3 (both strings have one distinct character):
- Loop through each character
cfrom 'a' to 'z'. Thiscis the target character. - For each
c, calculateopsby counting characters inaandbthat are not equal toc. - Update
min_ops = min(min_ops, ops).
- Loop through each character
- Return
min_ops.
Walkthrough
The problem asks for the minimum operations to satisfy one of three conditions. We can find the minimum cost for each condition independently and then take the minimum of these three costs.
-
Condition 1: All letters in
aare strictly less than all letters inb. This can be achieved by choosing a boundary characterc(from 'b' to 'z'). We then change all characters inathat are greater than or equal tocand all characters inbthat are less thanc. We iterate through all 25 possible boundariescfrom 'b' to 'z', calculate the cost for each, and take the minimum. -
Condition 2: All letters in
bare strictly less than all letters ina. This is symmetric to condition 1. We iterate through the same boundariescfrom 'b' to 'z'. For eachc, we change all characters inbthat are greater than or equal tocand all characters inathat are less thanc. -
Condition 3: Both
aandbconsist of only one distinct letter. This implies that all characters in both strings must be converted to the same characterc. We iterate through all 26 possible target characterscfrom 'a' to 'z'. The cost is the number of characters inanot equal tocplus the number of characters inbnot equal toc.
The final answer is the minimum cost found across all three conditions.
class Solution { public int minCharacters(String a, String b) { int n = a.length(); int m = b.length(); int minOps = n + m; // Max possible operations // Condition 1: a < b for (char c = 'b'; c <= 'z'; c++) { int currentOps = 0; for (char ch : a.toCharArray()) { if (ch >= c) { currentOps++; } } for (char ch : b.toCharArray()) { if (ch < c) { currentOps++; } } minOps = Math.min(minOps, currentOps); } // Condition 2: b < a for (char c = 'b'; c <= 'z'; c++) { int currentOps = 0; for (char ch : b.toCharArray()) { if (ch >= c) { currentOps++; } } for (char ch : a.toCharArray()) { if (ch < c) { currentOps++; } } minOps = Math.min(minOps, currentOps); } // Condition 3: a and b are uniform for (char c = 'a'; c <= 'z'; c++) { int currentOps = 0; for (char ch : a.toCharArray()) { if (ch != c) { currentOps++; } } for (char ch : b.toCharArray()) { if (ch != c) { currentOps++; } } minOps = Math.min(minOps, currentOps); } return minOps; }}Complexity
Time
O(K * (N + M)), where N and M are the lengths of strings `a` and `b`, and K is the size of the alphabet (26). Since K is a constant, the complexity is linear, O(N + M), but with a higher constant factor than the optimized approach.
Space
O(1), as we only use a few variables to store counts and the minimum operations.
Trade-offs
Pros
Simple to understand and implement.
Low memory usage as it only requires a few variables.
Cons
Less efficient than the optimized approach due to nested loops, which result in repeated traversals of the input strings.
The time complexity has a larger constant factor (26) compared to the prefix sum method.
This approach significantly optimizes the calculation by avoiding repeated traversals of the strings. It starts by creating frequency maps for both strings in a single pass. Then, it uses these maps to efficiently calculate the costs. For conditions 1 and 2, it further computes prefix sums on the frequency maps, which allows calculating the number of changes for any boundary character in O(1) time.
Algorithm
- Create frequency arrays
freqAandfreqBof size 26. - Populate
freqAandfreqBby iterating through stringsaandbonce. - Calculate
cost3, the minimum cost for condition 3. This isa.length() + b.length() - max(freqA[i] + freqB[i])forifrom 0 to 25. Initializemin_opswith this value. - Create prefix sum arrays
prefixAandprefixBfrom the frequency arrays.prefixA[i]will store the count of characters<= 'a' + i. - Loop
ifrom 0 to 24 (representing the split point after character'a' + i).- Calculate the cost for condition 1 at this split:
cost1_i = (a.length() - prefixA[i]) + prefixB[i]. - Calculate the cost for condition 2 at this split:
cost2_i = (b.length() - prefixB[i]) + prefixA[i]. - Update
min_ops = min(min_ops, cost1_i, cost2_i).
- Calculate the cost for condition 1 at this split:
- Return
min_ops.
Walkthrough
The core idea is to pre-calculate character frequencies to avoid re-scanning the strings for every possible case.
-
Frequency Maps: First, we iterate through strings
aandbonce to populate two frequency arrays,freqAandfreqB, of size 26.freqA[i]will hold the count of character'a' + iin stringa. -
Cost for Condition 3: The cost to make both strings uniform with character
cis(n - freqA[c-'a']) + (m - freqB[c-'a']). We can find the minimum cost for this condition by iterating through all 26 characters and finding the one that minimizes this value. This is equivalent to finding the charactercthat maximizes the sumfreqA[c-'a'] + freqB[c-'a']. -
Cost for Conditions 1 & 2: To quickly calculate the costs for conditions 1 and 2, we build prefix sum arrays (
prefixA,prefixB) from our frequency maps.prefixA[i]stores the total count of characters less than or equal to'a' + iin stringa.- For a given split point between character
iandi+1:- The cost for condition 1 (a < b) is the number of characters in
agreater thaniplus the number of characters inbless than or equal toi. This can be calculated in O(1) as(n - prefixA[i]) + prefixB[i]. - The cost for condition 2 (b < a) is calculated symmetrically as
(m - prefixB[i]) + prefixA[i].
- The cost for condition 1 (a < b) is the number of characters in
- We iterate through all 25 possible split points (from 'a'/'b' to 'y'/'z') and find the minimum operations for these two conditions.
- For a given split point between character
Finally, we return the minimum of the costs calculated for the three conditions.
class Solution { public int minCharacters(String a, String b) { int n = a.length(); int m = b.length(); int[] freqA = new int[26]; int[] freqB = new int[26]; for (char c : a.toCharArray()) { freqA[c - 'a']++; } for (char c : b.toCharArray()) { freqB[c - 'a']++; } // Condition 3: Both a and b consist of only one distinct letter int cost3 = n + m; for (int i = 0; i < 26; i++) { cost3 = Math.min(cost3, (n - freqA[i]) + (m - freqB[i])); } int minOps = cost3; // For conditions 1 and 2, use prefix sums int[] prefixA = new int[26]; int[] prefixB = new int[26]; prefixA[0] = freqA[0]; prefixB[0] = freqB[0]; for (int i = 1; i < 26; i++) { prefixA[i] = prefixA[i-1] + freqA[i]; prefixB[i] = prefixB[i-1] + freqB[i]; } // Iterate through possible split characters from 'b' to 'z' // A split after character 'i' means one string is <= 'a'+i and the other is > 'a'+i for (int i = 0; i < 25; i++) { // Condition 1: a < b // Changes in a: make all chars <= 'a'+i. Count of chars > 'a'+i is n - prefixA[i] // Changes in b: make all chars > 'a'+i. Count of chars <= 'a'+i is prefixB[i] int cost1 = (n - prefixA[i]) + prefixB[i]; minOps = Math.min(minOps, cost1); // Condition 2: b < a // Changes in b: make all chars <= 'a'+i. Count of chars > 'a'+i is m - prefixB[i] // Changes in a: make all chars > 'a'+i. Count of chars <= 'a'+i is prefixA[i] int cost2 = (m - prefixB[i]) + prefixA[i]; minOps = Math.min(minOps, cost2); } return minOps; }}Complexity
Time
O(N + M + K), where N and M are string lengths and K is the alphabet size (26). This simplifies to O(N + M) because the main work is the initial pass over the strings.
Space
O(K) for the frequency and prefix sum arrays, where K is the alphabet size (26). Since K is a fixed constant, this is considered O(1) space.
Trade-offs
Pros
Highly efficient, with a time complexity linear in the total length of the strings.
Processes each string only once, making it much faster for large inputs than the brute-force approach.
Cons
Slightly more complex to implement due to the use of auxiliary arrays for frequencies and prefix sums.
Uses a small amount of extra space for the arrays, though it's constant space.
Solutions
Solution
class Solution {private int ans;public int minCharacters(String a, String b) { int m = a.length(), n = b.length(); int[] cnt1 = new int[26]; int[] cnt2 = new int[26]; for (int i = 0; i < m; ++i) { ++cnt1[a.charAt(i) - 'a']; } for (int i = 0; i < n; ++i) { ++cnt2[b.charAt(i) - 'a']; } ans = m + n; for (int i = 0; i < 26; ++i) { ans = Math.min(ans, m + n - cnt1[i] - cnt2[i]); } f(cnt1, cnt2); f(cnt2, cnt1); return ans; }private void f(int[] cnt1, int[] cnt2) { for (int i = 1; i < 26; ++i) { int t = 0; for (int j = i; j < 26; ++j) { t += cnt1[j]; } for (int j = 0; j < i; ++j) { t += cnt2[j]; } ans = Math.min(ans, t); } }}class Solution {public: int minCharacters(string a, string b) { int m = a.size(), n = b.size(); vector<int> cnt1(26); vector<int> cnt2(26); for (char &c : a) ++cnt1[c - 'a']; for (char &c : b) ++cnt2[c - 'a']; int ans = m + n; for (int i = 0; i < 26; ++i) ans = min(ans, m + n - cnt1[i] - cnt2[i]); auto f = [&](vector<int> &cnt1, vector<int> &cnt2) { for (int i = 1; i < 26; ++i) { int t = 0; for (int j = i; j < 26; ++j) t += cnt1[j]; for (int j = 0; j < i; ++j) t += cnt2[j]; ans = min(ans, t); } }; f(cnt1, cnt2); f(cnt2, cnt1); return ans; }};class Solution: def minCharacters(self, a: str, b: str) -> int: def f(cnt1, cnt2): for i in range(1, 26): t = sum(cnt1[i:]) + sum(cnt2[: i]) nonlocal ans ans = min(ans, t) m, n = len(a), len(b) cnt1 = [0] * 26 cnt2 = [0] * 26 for c in a: cnt1[ord(c) - ord('a')] += 1 for c in b: cnt2[ord(c) - ord('a')] += 1 ans = m + n for c1, c2 in zip(cnt1, cnt2): ans = min(ans, m + n - c1 - c2) f(cnt1, cnt2) f(cnt2, cnt1) 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.