Count sorted digit groupings of a number

Last Updated : 13 May, 2026

Given a string s consisting only of digits, the task is to count the number of valid groupings possible.

  • A grouping is formed by splitting s into one or more non-empty contiguous substrings.
  • A grouping is considered valid if the sums of digits of the sub-groups form a non-decreasing sequence from left to right.

Examples:

Input: s = "1119"
Output: 7
Explanation:
One valid grouping is ["1", "11", "9"].
The sum of digits of the first sub-group ("1") is 1,
for the second sub-group ("11"), it is 2,
and for the third sub-group ("9"), it is 9.
Since the sums are in non-decreasing order (1 ≤ 2 ≤ 9), this is a valid grouping.
The other valid groupings are:
["1", "119"], ["1", "1", "19"], ["1", "1", "1", "9"], ["11", "19"], ["111", "9"], and ["1119"].
Thus, the total number of valid groupings is 7.

Input: s = "12"
Output: 2
Explanation:
["1","2"] and ["12"] are two valid groupings.

Input: s = "123"
Output: 4

Try It Yourself
redirect icon

[Naive Approach] Using Recursion - O(2^n) Time and O(n) Space

The idea is to recursively try every possible split by traversing from left to right.

  • Base Case: If the current index reaches the end of the string, we have formed a valid grouping, so return 1.
  • Recurrence Relation: At each position, try all substring splits from current index and recur if the current sum is ≥ previous sum.
C++
// C++ program to count the number of
// valid groupings using recursion
#include <bits/stdc++.h>
using namespace std;

// Recursive helper function to count valid groupings
int countWays(string &s, int index, int prevSum)
{
    // Base case: reached the end,
    // count as 1 valid grouping
    if (index == s.length())
    {
        return 1;
    }

    int res = 0;
    int currSum = 0;

    // Try every possible split starting from index
    for (int i = index; i < s.length(); i++)
    {
        // Add current digit to currSum
        currSum += s[i] - '0';

        // Proceed only if currSum is ≥ previous group sum
        if (currSum >= prevSum)
        {
            res += countWays(s, i + 1, currSum);
        }
    }

    return res;
}

int validGroups(string &s)
{
    // Start recursion from index
    // 0 with initial prevSum = 0
    return countWays(s, 0, 0);
}

// Driver code
int main()
{
    string s = "1119";

    cout << validGroups(s);

    return 0;
}
Java
import java.util.*;

class GFG {

    // Recursive helper function to count valid groupings
    static int countWays(String s, int index, int prevSum)
    {
        // Base case: reached the end,
        // count as 1 valid grouping
        if (index == s.length()) {
            return 1;
        }

        int res = 0;
        int currSum = 0;

        // Try every possible split starting from index
        for (int i = index; i < s.length(); i++) {

            // Add current digit to currSum
            currSum += s.charAt(i) - '0';

            // Proceed only if currSum is ≥ previous group
            // sum
            if (currSum >= prevSum) {
                res += countWays(s, i + 1, currSum);
            }
        }

        return res;
    }

    static int validGroups(String s)
    {
        // Start recursion from index
        // 0 with initial prevSum = 0
        return countWays(s, 0, 0);
    }

    public static void main(String[] args)
    {
        String s = "1119";

        System.out.println(validGroups(s));
    }
}
Python
# Recursive helper function to count valid groupings
def countWays(s, index, prevSum):

    # Base case: reached the end,
    # count as 1 valid grouping
    if index == len(s):
        return 1

    res = 0
    currSum = 0

    # Try every possible split starting from index
    for i in range(index, len(s)):

        # Add current digit to currSum
        currSum += int(s[i])

        # Proceed only if currSum is ≥ previous group sum
        if currSum >= prevSum:
            res += countWays(s, i + 1, currSum)

    return res


def validGroups(s):

    # Start recursion from index
    # 0 with initial prevSum = 0
    return countWays(s, 0, 0)


# Driver code
if __name__ == "__main__":
    s = "1119"
    print(validGroups(s))
C#
using System;

class GFG {

    // Recursive helper function to count valid groupings
    static int countWays(string s, int index, int prevSum)
    {
        // Base case: reached the end,
        // count as 1 valid grouping
        if (index == s.Length) {
            return 1;
        }

        int res = 0;
        int currSum = 0;

        // Try every possible split starting from index
        for (int i = index; i < s.Length; i++) {

            // Add current digit to currSum
            currSum += s[i] - '0';

            // Proceed only if currSum is ≥ previous group
            // sum
            if (currSum >= prevSum) {
                res += countWays(s, i + 1, currSum);
            }
        }

        return res;
    }

    static int validGroups(string s)
    {
        // Start recursion from index
        // 0 with initial prevSum = 0
        return countWays(s, 0, 0);
    }

    public static void Main()
    {
        string s = "1119";

        Console.WriteLine(validGroups(s));
    }
}
JavaScript
// Recursive helper function to count valid groupings
function countWays(s, index, prevSum)
{
    // Base case: reached the end,
    // count as 1 valid grouping
    if (index === s.length) {
        return 1;
    }

    let res = 0;
    let currSum = 0;

    // Try every possible split starting from index
    for (let i = index; i < s.length; i++) {

        // Add current digit to currSum
        currSum += Number(s[i]);

        // Proceed only if currSum is ≥ previous group sum
        if (currSum >= prevSum) {
            res += countWays(s, i + 1, currSum);
        }
    }

    return res;
}

function validGroups(s)
{
    // Start recursion from index
    // 0 with initial prevSum = 0
    return countWays(s, 0, 0);
}

// Driver code
let s = "1119";
console.log(validGroups(s));

Output
7

[Better Approach] Using Top-Down DP (Memoization) - O(n^3) Time and O(n^2) Space

Why do we use Dynamic Programming?

If we take a closer look at the above recursive solution, we notice that there may be overlapping subproblems. For example, if the input number is 12345, then for index = 2 and prevSum = 3, we recur two times. Similarly, for index 4 and prevSum = 7, we recur two times. Therefore the above solution can be optimized using Dynamic Programming.

What should be the dimensions and size of array?

  • There are two parameters that change in input, prevSum and index. So we need a two dimensional array.
  • The maximum sum of digits can be 9*length where 'length' is length of input num. So the maximum value for prevSum would be 9*prevSum and maximum value for index would be n.

The maximum sum of digits can be 9*length where 'length' is length of input number.

How do we track solved problems?

The solution first checks the memo table before recursive calls, ensuring that each subproblem is computed only once. We initialize the memo table as -1.

C++
// C++ code using Top-Down DP (Memoization)
// to count total number of valid groupings
#include <bits/stdc++.h>
using namespace std;

// Recursive function with memoization
int countWays(string &s, int index, int prevSum, vector<vector<int>> &memo)
{
    // Base case: if entire string is processed
    if (index == s.length())
    {
        return 1;
    }

    // If already computed, return memoized value
    if (memo[index][prevSum] != -1)
    {
        return memo[index][prevSum];
    }

    int currSum = 0;
    int total = 0;

    // Try all possible groupings starting from index
    for (int i = index; i < s.length(); i++)
    {
        // Add current digit to sum
        currSum += s[i] - '0';

        // Recurse only if non-decreasing sum condition holds
        if (currSum >= prevSum)
        {
            total += countWays(s, i + 1, currSum, memo);
        }
    }

    // Memoize and return
    memo[index][prevSum] = total;
    return total;
}

// Function to initialize memo and call the DP
int validGroups(string &s)
{
    int n = s.length();

    // memo[i][j]: number of ways from index i with prev sum j
    vector<vector<int>> memo(n + 1, vector<int>(n * 9, -1));

    return countWays(s, 0, 0, memo);
}

// Driver code
int main()
{
    string s = "1119";

    cout << validGroups(s) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {

    // Recursive function with memoization
    static int countWays(String s, int index, int prevSum,
                         int[][] memo)
    {
        // Base case: if entire string is processed
        if (index == s.length()) {
            return 1;
        }

        // If already computed, return memoized value
        if (memo[index][prevSum] != -1) {
            return memo[index][prevSum];
        }

        int currSum = 0;
        int total = 0;

        // Try all possible groupings starting from index
        for (int i = index; i < s.length(); i++) {

            // Add current digit to sum
            currSum += s.charAt(i) - '0';

            // Recurse only if non-decreasing sum condition
            // holds
            if (currSum >= prevSum) {
                total += countWays(s, i + 1, currSum, memo);
            }
        }

        // Memoize and return
        memo[index][prevSum] = total;
        return total;
    }

    // Function to initialize memo and call the DP
    static int validGroups(String s)
    {
        int n = s.length();

        // memo[i][j]: number of ways from index i with prev
        // sum j
        int[][] memo = new int[n + 1][n * 9];
        for (int[] row : memo)
            Arrays.fill(row, -1);

        return countWays(s, 0, 0, memo);
    }

    public static void main(String[] args)
    {
        String s = "1119";

        System.out.println(validGroups(s));
    }
}
Python
# Recursive function with memoization
def countWays(s, index, prevSum, memo):

    # Base case: if entire string is processed
    if index == len(s):
        return 1

    # If already computed, return memoized value
    if memo[index][prevSum] != -1:
        return memo[index][prevSum]

    currSum = 0
    total = 0

    # Try all possible groupings starting from index
    for i in range(index, len(s)):

        # Add current digit to sum
        currSum += int(s[i])

        # Recurse only if non-decreasing sum condition holds
        if currSum >= prevSum:
            total += countWays(s, i + 1, currSum, memo)

    # Memoize and return
    memo[index][prevSum] = total
    return total


# Function to initialize memo and call the DP
def validGroups(s):

    n = len(s)

    # memo[i][j]: number of ways from index i with prev sum j
    memo = [[-1] * (n * 9) for _ in range(n + 1)]

    return countWays(s, 0, 0, memo)


# Driver code
if __name__ == "__main__":
    s = "1119"
    print(validGroups(s))
C#
using System;

class GFG {

    // Recursive function with memoization
    static int countWays(string s, int index, int prevSum,
                         int[, ] memo)
    {
        // Base case: if entire string is processed
        if (index == s.Length) {
            return 1;
        }

        // If already computed, return memoized value
        if (memo[index, prevSum] != -1) {
            return memo[index, prevSum];
        }

        int currSum = 0;
        int total = 0;

        // Try all possible groupings starting from index
        for (int i = index; i < s.Length; i++) {

            // Add current digit to sum
            currSum += s[i] - '0';

            // Recurse only if non-decreasing sum condition
            // holds
            if (currSum >= prevSum) {
                total += countWays(s, i + 1, currSum, memo);
            }
        }

        // Memoize and return
        memo[index, prevSum] = total;
        return total;
    }

    // Function to initialize memo and call the DP
    static int validGroups(string s)
    {
        int n = s.Length;

        // memo[i][j]: number of ways from index i with prev
        // sum j
        int[, ] memo = new int[n + 1, n * 9];

        for (int i = 0; i <= n; i++)
            for (int j = 0; j < n * 9; j++)
                memo[i, j] = -1;

        return countWays(s, 0, 0, memo);
    }

    public static void Main()
    {
        string s = "1119";

        Console.WriteLine(validGroups(s));
    }
}
JavaScript
// Recursive function with memoization
function countWays(s, index, prevSum, memo)
{
    // Base case: if entire string is processed
    if (index === s.length) {
        return 1;
    }

    // If already computed, return memoized value
    if (memo[index][prevSum] !== -1) {
        return memo[index][prevSum];
    }

    let currSum = 0;
    let total = 0;

    // Try all possible groupings starting from index
    for (let i = index; i < s.length; i++) {

        // Add current digit to sum
        currSum += Number(s[i]);

        // Recurse only if non-decreasing sum condition
        // holds
        if (currSum >= prevSum) {
            total += countWays(s, i + 1, currSum, memo);
        }
    }

    // Memoize and return
    memo[index][prevSum] = total;
    return total;
}

// Function to initialize memo and call the DP
function validGroups(s)
{
    let n = s.length;

    // memo[i][j]: number of ways from index i with prev sum
    // j
    let memo = Array.from({length : n + 1},
                          () => new Array(n * 9).fill(-1));

    return countWays(s, 0, 0, memo);
}

// Driver code
let s = "1119";
console.log(validGroups(s));

Output
7

[Expected Approach] Using Bottom-Up DP (Tabulation) - O(n^3) Time and O(n^2) Space

The approach is similar to the previous one; we iteratively build up the solution in a bottom-up manner.

  • We maintain a dp table where dp[i][prevSum] stores the number of ways to split the substring starting from index i such that the previous group’s digit sum is prevSum.
  • Base Case: For any prevSum, dp[n][prevSum] = 1 because an empty suffix can be split in one valid way.
  • Recursive Case: For each index i and sum prevSum, we try all substrings starting at i, calculate the current digit sum, and add dp[j + 1][currSum] to dp[i][prevSum] if currSum ≥ prevSum.
C++
// C++ program using Tabulation (Bottom-Up DP)
// to count all valid groupings of digit string
#include <bits/stdc++.h>
using namespace std;

// Function to count valid groupings
int validGroups(string &s)
{
    int n = s.length();

    // Maximum possible sum of digits for a
    // string of length n is 9 * n
    int maxSum = n * 9;

    // dp[i][prevSum] => number of ways to split
    // from index i with last group sum = prevSum
    vector<vector<int>> dp(n + 1, vector<int>(maxSum + 1, 0));

    // Base Case: 1 valid way to split an empty string
    for (int prevSum = 0; prevSum <= maxSum; prevSum++)
    {
        dp[n][prevSum] = 1;
    }

    // Iterate from n-1 down to 0
    for (int i = n - 1; i >= 0; i--)
    {
        for (int prevSum = 0; prevSum <= maxSum; prevSum++)
        {

            int currSum = 0;

            // Try all substrings starting at index i
            for (int j = i; j < n; j++)
            {

                currSum += s[j] - '0';

                // Only proceed if sum is valid
                if (currSum >= prevSum)
                {
                    dp[i][prevSum] += dp[j + 1][currSum];
                }
            }
        }
    }

    // Start from index 0 with prevSum = 0
    return dp[0][0];
}

int main()
{
    string s = "1119";

    cout << validGroups(s) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {

    // Function to count valid groupings
    static int validGroups(String s)
    {
        int n = s.length();

        // Maximum possible sum of digits for a
        // string of length n is 9 * n
        int maxSum = n * 9;

        // dp[i][prevSum] => number of ways to split
        // from index i with last group sum = prevSum
        int[][] dp = new int[n + 1][maxSum + 1];

        // Base Case: 1 valid way to split an empty string
        for (int prevSum = 0; prevSum <= maxSum;
             prevSum++) {
            dp[n][prevSum] = 1;
        }

        // Iterate from n-1 down to 0
        for (int i = n - 1; i >= 0; i--) {
            for (int prevSum = 0; prevSum <= maxSum;
                 prevSum++) {

                int currSum = 0;

                // Try all substrings starting at index i
                for (int j = i; j < n; j++) {

                    currSum += s.charAt(j) - '0';

                    // Only proceed if sum is valid
                    if (currSum >= prevSum) {
                        dp[i][prevSum]
                            += dp[j + 1][currSum];
                    }
                }
            }
        }

        // Start from index 0 with prevSum = 0
        return dp[0][0];
    }

    public static void main(String[] args)
    {
        String s = "1119";

        System.out.println(validGroups(s));
    }
}
Python
# Python program using Tabulation (Bottom-Up DP)
# to count all valid groupings of digit string

# Function to count valid groupings
def validGroups(s):

    n = len(s)

    # Maximum possible sum of digits for a
    # string of length n is 9 * n
    maxSum = n * 9

    # dp[i][prevSum] => number of ways to split
    # from index i with last group sum = prevSum
    dp = [[0] * (maxSum + 1) for _ in range(n + 1)]

    # Base Case: 1 valid way to split an empty string
    for prevSum in range(maxSum + 1):
        dp[n][prevSum] = 1

    # Iterate from n-1 down to 0
    for i in range(n - 1, -1, -1):

        for prevSum in range(maxSum + 1):

            currSum = 0

            # Try all substrings starting at index i
            for j in range(i, n):

                currSum += ord(s[j]) - ord('0')

                # Only proceed if sum is valid
                if currSum >= prevSum:
                    dp[i][prevSum] += dp[j + 1][currSum]

    # Start from index 0 with prevSum = 0
    return dp[0][0]


# Driver code
if __name__ == "__main__":

    s = "1119"

    print(validGroups(s))
C#
using System;

class GFG {

    // Function to count valid groupings
    static int validGroups(string s)
    {
        int n = s.Length;

        // Maximum possible sum of digits for a
        // string of length n is 9 * n
        int maxSum = n * 9;

        // dp[i][prevSum] => number of ways to split
        // from index i with last group sum = prevSum
        int[, ] dp = new int[n + 1, maxSum + 1];

        // Base Case: 1 valid way to split an empty string
        for (int prevSum = 0; prevSum <= maxSum;
             prevSum++) {
            dp[n, prevSum] = 1;
        }

        // Iterate from n-1 down to 0
        for (int i = n - 1; i >= 0; i--) {
            for (int prevSum = 0; prevSum <= maxSum;
                 prevSum++) {

                int currSum = 0;

                // Try all substrings starting at index i
                for (int j = i; j < n; j++) {

                    currSum += s[j] - '0';

                    // Only proceed if sum is valid
                    if (currSum >= prevSum) {
                        dp[i, prevSum]
                            += dp[j + 1, currSum];
                    }
                }
            }
        }

        // Start from index 0 with prevSum = 0
        return dp[0, 0];
    }

    public static void Main()
    {
        string s = "1119";

        Console.WriteLine(validGroups(s));
    }
}
JavaScript
// JavaScript program using Tabulation (Bottom-Up DP)
// to count all valid groupings of digit string

// Function to count valid groupings
function validGroups(s)
{
    let n = s.length;

    // Maximum possible sum of digits for a
    // string of length n is 9 * n
    let maxSum = n * 9;

    // dp[i][prevSum] => number of ways to split
    // from index i with last group sum = prevSum
    let dp = Array.from({length : n + 1},
                        () => Array(maxSum + 1).fill(0));

    // Base Case: 1 valid way to split an empty string
    for (let prevSum = 0; prevSum <= maxSum; prevSum++) {
        dp[n][prevSum] = 1;
    }

    // Iterate from n-1 down to 0
    for (let i = n - 1; i >= 0; i--) {

        for (let prevSum = 0; prevSum <= maxSum;
             prevSum++) {

            let currSum = 0;

            // Try all substrings starting at index i
            for (let j = i; j < n; j++) {

                currSum += s[j].charCodeAt(0)
                           - "0".charCodeAt(0);

                // Only proceed if sum is valid
                if (currSum >= prevSum) {
                    dp[i][prevSum] += dp[j + 1][currSum];
                }
            }
        }
    }

    // Start from index 0 with prevSum = 0
    return dp[0][0];
}

// Driver code

let s = "1119";
console.log(validGroups(s));

Output
7
Comment