Count Number of Balanced Permutations
HardPrompt
You are given a string num. A string of digits is called balanced if the sum of the digits at even indices is equal to the sum of the digits at odd indices.
Return the number of distinct permutations of num that are balanced.
Since the answer may be very large, return it modulo 109 + 7.
A permutation is a rearrangement of all the characters of a string.
Example 1:
Input: num = "123"
Output: 2
Explanation:
- The distinct permutations of
numare"123","132","213","231","312"and"321". - Among them,
"132"and"231"are balanced. Thus, the answer is 2.
Example 2:
Input: num = "112"
Output: 1
Explanation:
- The distinct permutations of
numare"112","121", and"211". - Only
"121"is balanced. Thus, the answer is 1.
Example 3:
Input: num = "12345"
Output: 0
Explanation:
- None of the permutations of
numare balanced, so the answer is 0.
Constraints:
2 <= num.length <= 80numconsists of digits'0'to'9'only.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves generating every distinct permutation of the input string num. For each permutation generated, we then check if it satisfies the 'balanced' condition: the sum of digits at even indices equals the sum of digits at odd indices. We keep a count of all such balanced permutations.
Algorithm
- Create a frequency map (e.g., an array of size 10) to count the occurrences of each digit in the input string
num. - Implement a recursive backtracking function, say
generatePermutations(index, currentPermutation). - The base case for the recursion is when
indexreaches the length of the string,N. At this point, thecurrentPermutationis complete. - In the base case, check if the generated permutation is balanced. To do this, iterate through the permutation, summing digits at even and odd indices separately. If
sum_even == sum_odd, increment a global counter for balanced permutations. - In the recursive step, iterate through all possible digits (0-9). If the count of a digit in the frequency map is greater than 0, it's available for placement.
- Place the available digit at the current
indexof the permutation. Decrement its count in the frequency map. - Make a recursive call for the next index:
generatePermutations(index + 1, ...). - After the recursive call returns, backtrack by restoring the count of the digit in the frequency map. This allows the digit to be used in different positions in other permutations.
- The initial call to the function would be
generatePermutations(0, new char[N]). - The final value of the counter is the answer. Note that this approach does not handle the modulo operation as it's computationally infeasible to reach large numbers that would require it.
Walkthrough
The most straightforward way to solve this problem is to generate all possible unique arrangements of the digits in num and test each one for the balanced property.
We can use a recursive backtracking algorithm to explore all permutations. To handle duplicate digits in num and ensure that we only generate distinct permutations, we first count the frequency of each digit. The recursive function then builds a permutation character by character. At each step, it tries to place an available digit (one whose count is greater than zero) and then recurses. After the recursive call, it backtracks by undoing the choice.
Once a full permutation is formed (i.e., the recursion reaches its base case), we check if it's balanced by summing the digits at even and odd positions and comparing the sums. If they are equal, we increment our answer.
import java.util.Arrays; class Solution { long count = 0; int n; public int countBalancedPermutations(String num) { this.n = num.length(); int[] freq = new int[10]; for (char c : num.toCharArray()) { freq[c - '0']++; } generate(0, new char[n], freq); return (int) count; } private void generate(int index, char[] p, int[] freq) { if (index == n) { if (isBalanced(p)) { count++; } return; } for (int i = 0; i <= 9; i++) { if (freq[i] > 0) { p[index] = (char) (i + '0'); freq[i]--; generate(index + 1, p, freq); freq[i]++; // backtrack } } } private boolean isBalanced(char[] p) { int evenSum = 0; int oddSum = 0; for (int i = 0; i < p.length; i++) { if (i % 2 == 0) { evenSum += p[i] - '0'; } else { oddSum += p[i] - '0'; } } return evenSum == oddSum; }}This approach is far too slow for the given constraints due to the factorial growth in the number of permutations.
Complexity
Time
O((N! / Π(count_i!)) * N). The number of distinct permutations is given by the multinomial coefficient, and for each permutation, we perform an O(N) check. This is prohibitively expensive for N up to 80.
Space
O(N) for the recursion depth and to store the current permutation being built.
Trade-offs
Pros
Conceptually simple and easy to understand.
Correct for very small input sizes.
Cons
Extremely inefficient and will time out for all but the smallest inputs.
The number of permutations can be massive (up to 80!), making this approach computationally infeasible.
It's not practical for the given constraints (
num.length <= 80).
A more efficient approach uses dynamic programming combined with combinatorics. The problem can be reframed as partitioning the multiset of digits from num into two sub-multisets: one for even positions and one for odd positions. For a permutation to be balanced, the sum of digits in both sub-multisets must be equal to half of the total sum of all digits.
The core idea is to calculate the number of ways to choose a sub-multiset of n_even digits (for the even positions) that sums up to a target value T. Then, for each valid partition, we calculate the number of distinct permutations of the digits in the even positions and the odd positions. Summing these up for all valid partitions gives the total count.
The number of ways to form these partitions can be calculated with a DP approach. The final result is then found by combining the DP result with permutation counts using factorials.
Algorithm
- Precompute factorials and their modular inverses up to
Nto calculate combinationsC(n, k)in O(1) time. - Count the frequency of each digit in
numinto acountsarray. Calculate the total sum of digits,S. - If
Sis odd, it's impossible to partition it into two equal halves, so return 0. - Determine the number of even positions
n_even = (N + 1) / 2, odd positionsn_odd = N / 2, and the target sum for each partitionT = S / 2. - Initialize a 2D DP table
dp[n_even + 1][T + 1].dp[j][k]will store the sum of combinatorial products for choosingjitems with sumk. Setdp[0][0] = 1. - Iterate through each digit
dfrom 0 to 9. For each digit, use its countcounts[d]to update thedptable. This is done by creating anew_dptable for the current iteration. - The update rule is:
new_dp[j_new][k_new] += dp[j_prev][k_prev] * C(counts[d], c). This is iterated for all previous states(j_prev, k_prev), and for all numbers of copiescof digitdfrom 0 tocounts[d]. - After iterating through all digits,
dp[n_even][T]will holdsum_P(Π C(counts[d], c_d)), which is the sum of ways to choose the multisets for the even positions. - The final answer is calculated using the formula:
Total = (dp[n_even][T] * n_even! * n_odd!) / (Π counts[d]!) mod (10^9 + 7). This combines the number of ways to choose the partitions with the number of ways to permute the digits within those partitions.
Walkthrough
This problem can be solved by breaking it down into a selection part and a permutation part.
1. Problem Reframing
Let N be the length of num, S be the total sum of its digits. A permutation is balanced if sum_even = sum_odd. Since sum_even + sum_odd = S, this implies 2 * sum_even = S. Thus, S must be even. If S is odd, no solution exists, and we can return 0.
If S is even, the target sum for both even and odd positions is T = S / 2. The number of even positions is n_even = (N + 1) / 2 and odd positions is n_odd = N / 2.
We need to choose n_even digits from the multiset of digits in num that sum to T. The remaining n_odd digits will automatically sum to T.
2. Combinatorial Formula
For a specific partition of digits into an even_set and an odd_set, the number of distinct permutations is (Permutations of even_set) * (Permutations of odd_set).
Let counts[d] be the frequency of digit d in num. If we choose c_d copies of digit d for the even set, then counts[d] - c_d copies go to the odd set.
The number of permutations for this partition is (n_even! / Π c_d!) * (n_odd! / Π (counts[d] - c_d)!).
Summing over all valid partitions P (where Σ c_d = n_even and Σ d*c_d = T), we get:
Total = Σ_P (n_even! * n_odd!) / (Π c_d! * (counts[d] - c_d)!)
Using C(n, k) = n! / (k! * (n-k)!), this simplifies to:
Total = (n_even! * n_odd! / Π counts[d]!) * Σ_P (Π C(counts[d], c_d))
3. Dynamic Programming
We can compute Sum_Term = Σ_P (Π C(counts[d], c_d)) using DP.
Let dp[j][k] be the sum of Π C(counts[d], c_d) for partitions of digits considered so far, choosing j digits with a total sum of k.
- State:
dp[j][k] - Transition: We iterate through each digit
dfrom 0 to 9. We update thedptable by considering takingccopies of digitd(where0 <= c <= counts[d]).dp_new[j_prev+c][k_prev+c*d] += dp_old[j_prev][k_prev] * C(counts[d], c) - Base Case:
dp[0][0] = 1(choosing 0 items gives sum 0 in one way).
After iterating through all digits, dp[n_even][T] will hold the Sum_Term.
4. Final Calculation
The final answer is (dp[n_even][T] * n_even! * n_odd! * Π (1/counts[d]!)) mod (10^9 + 7).
class Solution { long MOD = 1_000_000_007; long[] fact; long[] invFact; public int countBalancedPermutations(String num) { int velunexorai = num.length(); int n = velunexorai; int[] counts = new int[10]; int totalSum = 0; for (char c : num.toCharArray()) { int digit = c - '0'; counts[digit]++; totalSum += digit; } if (totalSum % 2 != 0) { return 0; } int targetSum = totalSum / 2; int evenLen = (n + 1) / 2; int oddLen = n / 2; precomputeFactorials(n); long[][] dp = new long[evenLen + 1][targetSum + 1]; dp[0][0] = 1; for (int d = 0; d < 10; d++) { if (counts[d] == 0) continue; long[][] newDp = new long[evenLen + 1][targetSum + 1]; for (int j = 0; j <= evenLen; j++) { for (int k = 0; k <= targetSum; k++) { if (dp[j][k] == 0) continue; for (int c = 0; c <= counts[d]; c++) { if (j + c <= evenLen && k + c * d <= targetSum) { long ways = (dp[j][k] * C(counts[d], c)) % MOD; newDp[j + c][k + c * d] = (newDp[j + c][k + c * d] + ways) % MOD; } } } } dp = newDp; } long sumComb = dp[evenLen][targetSum]; long result = (sumComb * fact[evenLen]) % MOD; result = (result * fact[oddLen]) % MOD; for (int count : counts) { result = (result * invFact[count]) % MOD; } return (int) result; } private void precomputeFactorials(int n) { fact = new long[n + 1]; invFact = new long[n + 1]; fact[0] = 1; invFact[0] = 1; for (int i = 1; i <= n; i++) { fact[i] = (fact[i - 1] * i) % MOD; invFact[i] = power(fact[i], MOD - 2); } } private long C(int n, int k) { if (k < 0 || k > n) return 0; return (((fact[n] * invFact[k]) % MOD) * invFact[n - k]) % MOD; } private long power(long base, long exp) { long res = 1; base %= MOD; while (exp > 0) { if (exp % 2 == 1) res = (res * base) % MOD; base = (base * base) % MOD; exp /= 2; } return res; }}Complexity
Time
O(D * n_even * T * max_count), where D=10 is the number of digits, `n_even` is O(N), `T` is O(N * 9), and `max_count` is O(N). This gives a polynomial time complexity, roughly O(N^2 * S). Given the constraints, this is approximately `10 * 41 * 361 * 81` operations, which is acceptable.
Space
O(n_even * T) which is O(N * S), where S is the total sum. For the given constraints, this is approximately `41 * 361`, which is manageable.
Trade-offs
Pros
Provides a feasible solution that passes within the time limits for the given constraints.
Correctly handles all aspects of the problem including permutations of multisets and large numbers using modular arithmetic.
It's a standard technique for this class of combinatorial counting problems.
Cons
The state space of the DP table can be large,
O(N * S).Requires careful handling of modular arithmetic, including precomputation of factorials and modular inverses.
The four nested loops in the DP update might seem intimidating, but it's feasible within the time limits.
Solutions
Solution
class Solution {private final int[] cnt = new int[10];private final int mod = (int)1 e9 + 7;private Integer[][][][] f;private long[][] c;public int countBalancedPermutations(String num) { int s = 0; for (char c : num.toCharArray()) { cnt[c - '0']++; s += c - '0'; } if (s % 2 == 1) { return 0; } int n = num.length(); int m = n / 2 + 1; f = new Integer[10][s / 2 + 1][m][m + 1]; c = new long[m + 1][m + 1]; c[0][0] = 1; for (int i = 1; i <= m; i++) { c[i][0] = 1; for (int j = 1; j <= i; j++) { c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod; } } return dfs(0, s / 2, n / 2, (n + 1) / 2); }private int dfs(int i, int j, int a, int b) { if (i > 9) { return ((j | a | b) == 0) ? 1 : 0; } if (a == 0 && j != 0) { return 0; } if (f[i][j][a][b] != null) { return f[i][j][a][b]; } int ans = 0; for (int l = 0; l <= Math.min(cnt[i], a); ++l) { int r = cnt[i] - l; if (r >= 0 && r <= b && l * i <= j) { int t = (int)(c[a][l] * c[b][r] % mod * dfs(i + 1, j - l * i, a - l, b - r) % mod); ans = (ans + t) % mod; } } return f[i][j][a][b] = ans; }}using ll = long long ; const int MX = 80 ; const int MOD = 1e9 + 7 ; ll c [ MX ][ MX ]; auto init = [] { c [ 0 ][ 0 ] = 1 ; for ( int i = 1 ; i < MX ; ++ i ) { c [ i ][ 0 ] = 1 ; for ( int j = 1 ; j <= i ; ++ j ) { c [ i ][ j ] = ( c [ i - 1 ][ j ] + c [ i - 1 ][ j - 1 ]) % MOD ; } } return 0 ; }(); class Solution { public: int countBalancedPermutations ( string num ) { int cnt [ 10 ]{}; int s = 0 ; for ( char & c : num ) { ++ cnt [ c - '0' ]; s += c - '0' ; } if ( s % 2 ) { return 0 ; } int n = num . size (); int m = n / 2 + 1 ; int f [ 10 ][ s / 2 + 1 ][ m ][ m + 1 ]; memset ( f , - 1 , sizeof ( f )); auto dfs = [ & ]( auto && dfs , int i , int j , int a , int b ) -> int { if ( i > 9 ) { return (( j | a | b ) == 0 ? 1 : 0 ); } if ( a == 0 && j ) { return 0 ; } if ( f [ i ][ j ][ a ][ b ] != - 1 ) { return f [ i ][ j ][ a ][ b ]; } int ans = 0 ; for ( int l = 0 ; l <= min ( cnt [ i ], a ); ++ l ) { int r = cnt [ i ] - l ; if ( r >= 0 && r <= b && l * i <= j ) { int t = c [ a ][ l ] * c [ b ][ r ] % MOD * dfs ( dfs , i + 1 , j - l * i , a - l , b - r ) % MOD ; ans = ( ans + t ) % MOD ; } } return f [ i ][ j ][ a ][ b ] = ans ; }; return dfs ( dfs , 0 , s / 2 , n / 2 , ( n + 1 ) / 2 ); } };class Solution: def countBalancedPermutations(self, num: str) -> int: @ cache def dfs(i: int, j: int, a: int, b: int) -> int: if i > 9: return (j | a | b) == 0 if a == 0 and j: return 0 ans = 0 for l in range(min(cnt[i], a) + 1): r = cnt[i] - l if 0 <= r <= b and l * i <= j: t = comb(a, l) * comb(b, r) * dfs(i + 1, j - l * i, a - l, b - r) ans = (ans + t) % mod return ans nums = list(map(int, num)) s = sum(nums) if s % 2: return 0 n = len(nums) mod = 10 ** 9 + 7 cnt = Counter(nums) return dfs(0, s // 2, n // 2, (n + 1) // 2)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.