Split With Minimum Sum
EasyPrompt
Given a positive integer num, split it into two non-negative integers num1 and num2 such that:
- The concatenation of
num1andnum2is a permutation ofnum.- In other words, the sum of the number of occurrences of each digit in
num1andnum2is equal to the number of occurrences of that digit innum.
- In other words, the sum of the number of occurrences of each digit in
num1andnum2can contain leading zeros.
Return the minimum possible sum of num1 and num2.
Notes:
- It is guaranteed that
numdoes not contain any leading zeros. - The order of occurrence of the digits in
num1andnum2may differ from the order of occurrence ofnum.
Example 1:
num1Example 2:
num1
Constraints:
10 <= num <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
This approach systematically explores every possible way to divide the digits of the input number num into two separate groups. For each possible division (or partition), it constructs the two smallest possible numbers, num1 and num2, by arranging the digits in each group in ascending order. It then calculates their sum and keeps track of the minimum sum found across all partitions. While exhaustive and guaranteed to be correct, this method is computationally more intensive than the optimal greedy solution.
Algorithm
-
- Extract the digits of the input
numinto a list.
- Extract the digits of the input
-
- Implement a recursive function
generatePartitionsthat takes the current digit's index and the lists fornum1andnum2.
- Implement a recursive function
-
- In the recursive function, for each digit, explore two branches: adding it to
list1or adding it tolist2.
- In the recursive function, for each digit, explore two branches: adding it to
-
- The base case for the recursion is when all digits have been assigned. At this point, check if both lists are non-empty.
-
- If they are, build the smallest possible numbers from
list1andlist2by sorting their digits and concatenating them.
- If they are, build the smallest possible numbers from
-
- Calculate the sum of these two numbers and update a global minimum sum.
-
- After the recursion completes, the global minimum sum is the result.
Walkthrough
The core idea is to treat the problem as finding the best partition of a set of digits. We can use a recursive backtracking algorithm to generate all possible partitions.
For an input number like 4325, the digits are {4, 3, 2, 5}. The algorithm will explore partitions like:
{4}and{3, 2, 5}->num1=4,num2=235, sum=239{4, 3}and{2, 5}->num1=34,num2=25, sum=59- ... and so on for all
2^(k-1) - 1non-trivial partitions.
For each partition, we form the smallest numbers possible. If a partition for num1 is {5, 2}, the smallest number we can form is 25. This is done by sorting the digits within each partition. The algorithm maintains a global minimum, updating it whenever a smaller sum is found.
Here is a code snippet demonstrating the recursive partitioning:
import java.util.*; class Solution { long minSum = Long.MAX_VALUE; public int splitNum(int num) { String s = Integer.toString(num); List<Character> digits = new ArrayList<>(); for (char c : s.toCharArray()) { digits.add(c); } // Start the recursive partitioning from the first digit. generatePartitions(0, digits, new ArrayList<>(), new ArrayList<>()); return (int) minSum; } private void generatePartitions(int index, List<Character> allDigits, List<Character> list1, List<Character> list2) { // Base case: all digits have been assigned. if (index == allDigits.size()) { // Ensure both numbers are non-empty as per the problem's implicit split. if (!list1.isEmpty() && !list2.isEmpty()) { long num1 = buildSmallestNumber(list1); long num2 = buildSmallestNumber(list2); minSum = Math.min(minSum, num1 + num2); } return; } char currentDigit = allDigits.get(index); // Option 1: Add the current digit to the first number's list. list1.add(currentDigit); generatePartitions(index + 1, allDigits, list1, list2); list1.remove(list1.size() - 1); // Backtrack to explore other possibilities. // Option 2: Add the current digit to the second number's list. list2.add(currentDigit); generatePartitions(index + 1, allDigits, list1, list2); list2.remove(list2.size() - 1); // Backtrack. } // Helper function to build the smallest number from a list of digits. private long buildSmallestNumber(List<Character> digitList) { Collections.sort(digitList); StringBuilder sb = new StringBuilder(); for (char c : digitList) { sb.append(c); } return Long.parseLong(sb.toString()); }}Complexity
Time
O(2^k * k log k), where `k` is the number of digits in `num`. For each of the `2^k` ways to partition the digits, we sort the two resulting sub-lists. Since `k <= 10`, this is acceptable.
Space
O(k) for the recursion depth and to store the lists of digits, where `k` is the number of digits.
Trade-offs
Pros
It is a correct approach as it exhaustively checks all valid splits of the digits.
It serves as a good baseline to verify the correctness of more optimized solutions.
Cons
The time complexity is exponential, making it inefficient for larger numbers of digits (though feasible for this problem's constraints).
The implementation is significantly more complex than the greedy approach due to recursion and backtracking.
This is the most efficient and elegant solution. The core insight is that to minimize the sum num1 + num2, we should construct num1 and num2 to be as small as possible. A number's magnitude is primarily determined by its most significant digits and its length. By sorting all available digits and then distributing them one by one, alternating between num1 and num2, we achieve two things:
- The smallest digits (
d1,d2) are placed in the most significant positions ofnum1andnum2. - The lengths of
num1andnum2are kept as balanced as possible (differing by at most one). This greedy strategy correctly yields the minimum possible sum.
Algorithm
-
- Convert the input integer
numto a string, then to a character array.
- Convert the input integer
-
- Sort the character array in ascending order.
-
- Initialize two string builders,
s1ands2.
- Initialize two string builders,
-
- Loop through the sorted character array from
i = 0tolength - 1.
- Loop through the sorted character array from
-
- If
iis an even index, append the digitdigits[i]tos1.
- If
-
- If
iis an odd index, append the digitdigits[i]tos2.
- If
-
- After the loop, convert
s1ands2to integers.
- After the loop, convert
-
- Return the sum of the two integers.
Walkthrough
Let's walk through an example: num = 4325.
- Extract and Sort Digits: The digits are
4, 3, 2, 5. Sorting them gives2, 3, 4, 5. - Distribute Alternately: We build two new numbers,
num1andnum2.- The first smallest digit,
2, goes tonum1. (num1= "2") - The second smallest digit,
3, goes tonum2. (num2= "3") - The third smallest digit,
4, goes tonum1. (num1= "24") - The fourth smallest digit,
5, goes tonum2. (num2= "35")
- The first smallest digit,
- Calculate Sum: We have
num1 = 24andnum2 = 35. Their sum is24 + 35 = 59.
This process guarantees that the smallest digits contribute to the higher place values (tens, hundreds, etc.), thus minimizing the overall sum. The implementation is straightforward.
Here is the Java code for this approach:
import java.util.Arrays; class Solution { public int splitNum(int num) { // Convert the number to a character array to easily access and sort digits. char[] digits = Integer.toString(num).toCharArray(); // Sort the digits in ascending order. Arrays.sort(digits); // Initialize two string builders to construct the two new numbers. StringBuilder num1Str = new StringBuilder(); StringBuilder num2Str = new StringBuilder(); // Iterate through the sorted digits and distribute them alternately. for (int i = 0; i < digits.length; i++) { if (i % 2 == 0) { // Even-indexed digits (0, 2, ...) go to the first number. num1Str.append(digits[i]); } else { // Odd-indexed digits (1, 3, ...) go to the second number. num2Str.append(digits[i]); } } // Convert the resulting strings to integers. int num1 = Integer.parseInt(num1Str.toString()); int num2 = Integer.parseInt(num2Str.toString()); // Return the sum. return num1 + num2; }}Complexity
Time
O(k log k), where `k` is the number of digits in `num`. The dominant operation is sorting the `k` digits. Since `k` is small (at most 10), this is very fast.
Space
O(k) to store the character array for the digits, where `k` is the number of digits.
Trade-offs
Pros
Extremely efficient with a near-linear time complexity.
Simple and concise to implement.
Directly solves the problem without unnecessary exploration of non-optimal solutions.
Cons
The correctness of the greedy choice is not immediately obvious and relies on the insight about minimizing numbers by placing small digits in high-value places.
Solutions
Solution
class Solution {public int splitNum(int num) { int[] cnt = new int[10]; int n = 0; for (; num > 0; num /= 10) { ++cnt[num % 10]; ++n; } int[] ans = new int[2]; for (int i = 0, j = 0; i < n; ++i) { while (cnt[j] == 0) { ++j; } --cnt[j]; ans[i & 1] = ans[i & 1] * 10 + j; } return ans[0] + ans[1]; }}class Solution: def splitNum(self, num: int) -> int: cnt = Counter() n = 0 while num: cnt[num % 10] += 1 num //= 10 n += 1 ans = [0] * 2 j = 0 for i in range(n): while cnt[j] == 0: j += 1 cnt[j] -= 1 ans[i & 1] = ans[i & 1] * 10 + j return sum(ans)class Solution {public: int splitNum(int num) { int cnt[10]{}; int n = 0; for (; num; num /= 10) { ++cnt[num % 10]; ++n; } int ans[2]{}; for (int i = 0, j = 0; i < n; ++i) { while (cnt[j] == 0) { ++j; } --cnt[j]; ans[i & 1] = ans[i & 1] * 10 + j; } return ans[0] + ans[1]; }};Video 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.