Number of subsequences of the form a^i b^j c^k

Last Updated : 6 Sep, 2026

Given a string s consisting of lowercase English letters, count the number of subsequences that follow the pattern ai bj ck some number of a's, followed by some number of b's, followed by some number of c's, with i >= 1, j >= 1, k >= 1.

Note:

  • Subsequences are considered different if they use a different set of indices, even if the characters look the same.
  • Since the answer can be large, return it modulo 109+7.

Examples:

Input: s = "abbc"
Output: 3
Explanation: The string has one 'a' (index 0), two 'b's (indices 1 and 2), and one 'c' (index 3). The valid subsequences are:
- a(0) + b(1) + c(3) -> "abc"
- a(0) + b(2) + c(3) -> "abc"
- a(0) + b(1)b(2) + c(3) -> "abbc"
These are counted as 3 distinct subsequences since each uses a different set of indices, even though the first two look identical as strings.

Input: s = "abcabc"
Output: 7
Explanation: The string has two 'a's (indices 0, 3), two 'b's (indices 1, 4), and two 'c's (indices 2, 5). Every valid combination of at least one a, at least one b (all chosen after every chosen a), and at least one c (all chosen after every chosen b) is counted separately. Working through all valid index combinations gives a total of 7 such subsequences.

Try It Yourself
redirect icon

[Naive Approach] Enumerate Every Subset - O(2^n * n) Time and O(n) Space

Generate all subset of indices, then test whether it splits into three non-empty parts of only a's, then only b's, then only c's.

Illustration:

  • Take s = "abbc" (indices 0='a', 1='b', 2='b', 3='c').
  • Trying the subset {0, 1, 3} gives "abc", which splits into "a" + "b" + "c", all three parts non-empty and matching their required character - valid.
  • Trying the subset {0, 2, 3} gives "abc" as well (using the second 'b' instead), also valid, and counted separately since it uses a different set of indices.
  • Trying the subset {0, 1, 2, 3} gives "abbc", which splits into "a" + "bb" + "c", also valid.
  • Every other subset either fails to contain at least one of each required character, or doesn't produce characters in the right order, so only these 3 subsets are valid, matching the expected output.
C++
#include <bits/stdc++.h>
using namespace std;

int countSub(string &s) {
    int n = s.length();
    long long count = 0;
    const int MOD = 1e9 + 7;

    // try every possible subset of indices
    for (int mask = 1; mask < (1 << n); mask++) {
        string sub = "";
        for (int i = 0; i < n; i++) {
            if (mask & (1 << i))
                sub += s[i];
        }

        int len = sub.length();
        bool valid = false;

        // try every way to split the subsequence into three non-empty parts
        for (int i = 1; i < len && !valid; i++) {
            for (int j = i; j < len && !valid; j++) {
                bool allA = true, allB = true, allC = true;

                for (int k = 0; k < i; k++)
                    if (sub[k] != 'a') allA = false;
                for (int k = i; k < j; k++)
                    if (sub[k] != 'b') allB = false;
                for (int k = j; k < len; k++)
                    if (sub[k] != 'c') allC = false;

                if (allA && allB && allC && i >= 1 && (j - i) >= 1 && (len - j) >= 1)
                    valid = true;
            }
        }

        if (valid)
            count = (count + 1) % MOD;
    }

    return (int) count;
}

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

    cout << countSub(s) << endl;

    return 0;
}
Java
class GfG {
    static int countSub(String s) {
        int n = s.length();
        long count = 0;
        final int MOD = 1000000007;

        // try every possible subset of indices
        for (int mask = 1; mask < (1 << n); mask++) {
            StringBuilder sub = new StringBuilder();
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0)
                    sub.append(s.charAt(i));
            }

            int len = sub.length();
            boolean valid = false;

            // try every way to split the subsequence into three non-empty parts
            for (int i = 1; i < len && !valid; i++) {
                for (int j = i; j < len && !valid; j++) {
                    boolean allA = true, allB = true, allC = true;

                    for (int k = 0; k < i; k++)
                        if (sub.charAt(k) != 'a') allA = false;
                    for (int k = i; k < j; k++)
                        if (sub.charAt(k) != 'b') allB = false;
                    for (int k = j; k < len; k++)
                        if (sub.charAt(k) != 'c') allC = false;

                    if (allA && allB && allC && i >= 1 && (j - i) >= 1 && (len - j) >= 1)
                        valid = true;
                }
            }

            if (valid)
                count = (count + 1) % MOD;
        }

        return (int) count;
    }

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

        System.out.println(countSub(s));
    }
}
Python
def countSub(s):
    n = len(s)
    count = 0
    MOD = 10**9 + 7

    # try every possible subset of indices
    for mask in range(1, 1 << n):
        sub = ""
        for i in range(n):
            if mask & (1 << i):
                sub += s[i]

        length = len(sub)
        valid = False

        # try every way to split the subsequence into three non-empty parts
        for i in range(1, length):
            if valid:
                break
            for j in range(i, length):
                if valid:
                    break
                allA = all(c == 'a' for c in sub[:i])
                allB = all(c == 'b' for c in sub[i:j])
                allC = all(c == 'c' for c in sub[j:])

                if allA and allB and allC and i >= 1 and (j - i) >= 1 and (length - j) >= 1:
                    valid = True

        if valid:
            count = (count + 1) % MOD

    return count

s = "abbc"
print(countSub(s))
C#
using System;
using System.Text;

class GfG {
    static int CountSub(string s) {
        int n = s.Length;
        long count = 0;
        const int MOD = 1000000007;

        // try every possible subset of indices
        for (int mask = 1; mask < (1 << n); mask++) {
            StringBuilder sub = new StringBuilder();
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0)
                    sub.Append(s[i]);
            }

            int len = sub.Length;
            bool valid = false;

            // try every way to split the subsequence into three non-empty parts
            for (int i = 1; i < len && !valid; i++) {
                for (int j = i; j < len && !valid; j++) {
                    bool allA = true, allB = true, allC = true;

                    for (int k = 0; k < i; k++)
                        if (sub[k] != 'a') allA = false;
                    for (int k = i; k < j; k++)
                        if (sub[k] != 'b') allB = false;
                    for (int k = j; k < len; k++)
                        if (sub[k] != 'c') allC = false;

                    if (allA && allB && allC && i >= 1 && (j - i) >= 1 && (len - j) >= 1)
                        valid = true;
                }
            }

            if (valid)
                count = (count + 1) % MOD;
        }

        return (int) count;
    }

    static void Main() {
        string s = "abbc";

        Console.WriteLine(CountSub(s));
    }
}
JavaScript
function countSub(s) {
    const n = s.length;
    let count = 0n;
    const MOD = 1000000007n;

    // try every possible subset of indices
    for (let mask = 1; mask < (1 << n); mask++) {
        let sub = "";
        for (let i = 0; i < n; i++) {
            if (mask & (1 << i))
                sub += s[i];
        }

        const len = sub.length;
        let valid = false;

        // try every way to split the subsequence into three non-empty parts
        for (let i = 1; i < len && !valid; i++) {
            for (let j = i; j < len && !valid; j++) {
                let allA = true, allB = true, allC = true;

                for (let k = 0; k < i; k++)
                    if (sub[k] !== 'a') allA = false;
                for (let k = i; k < j; k++)
                    if (sub[k] !== 'b') allB = false;
                for (let k = j; k < len; k++)
                    if (sub[k] !== 'c') allC = false;

                if (allA && allB && allC && i >= 1 && (j - i) >= 1 && (len - j) >= 1)
                    valid = true;
            }
        }

        if (valid)
            count = (count + 1n) % MOD;
    }

    return Number(count);
}

// Driver Code
const s = "abbc";
console.log(countSub(s));

Output
3

[Expected Approach] Counting with Running Totals - O(n) Time and O(1) Space

Three running counts are maintained while scanning once: valid a-only subsequences, a-followed-by-b subsequences, and full a-b-c subsequences. Each new character either extends existing subsequences from the level below or starts a fresh one, giving a doubling update at each step.

Illustration:

  • Take s = "abbc".
  • After 'a' (index 0): countA becomes 1 (just the single 'a').
  • After 'b' (index 1): countAB becomes 2 * 0 + 1 = 1 (the pair "a"+"b" using this b).
  • After 'b' (index 2): countAB becomes 2 * 1 + 1 = 3 (existing pair extends to include this b, giving "a"+"bb"; a new pair forms using just this b, giving "a"+"b"; and the original pair "a"+"b" from before remains valid on its own).
  • After 'c' (index 3): countABC becomes 2 * 0 + 3 = 3 (three ways to pair the existing a-b combinations with this c).
  • Final answer: 3, matching the expected output.
C++
#include <bits/stdc++.h>
using namespace std;

int countSub(string &s) {
    const int MOD = 1e9 + 7;
    long long a = 0, ab = 0, abc = 0;

    for (char ch : s) {
        if (ch == 'a') {
            // every existing a-subset can include this 'a' or not, plus this 'a' alone
            a = (2 * a + 1) % MOD;
        } else if (ch == 'b') {
            // every existing ab-pair can extend with this 'b' or not, plus new pairs using this 'b'
            ab = (2 * ab + a) % MOD;
        } else if (ch == 'c') {
            // same doubling pattern one level up
            abc = (2 * abc + ab) % MOD;
        }
    }

    return (int) abc;
}

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

    cout << countSub(s) << endl;

    return 0;
}
Java
class GfG {
    static int countSub(String s) {
        final int MOD = 1000000007;
        long a = 0, ab = 0, abc = 0;

        for (char ch : s.toCharArray()) {
            if (ch == 'a') {
                a = (2 * a + 1) % MOD;
            } else if (ch == 'b') {
                ab = (2 * ab + a) % MOD;
            } else if (ch == 'c') {
                abc = (2 * abc + ab) % MOD;
            }
        }

        return (int) abc;
    }

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

        System.out.println(countSub(s));
    }
}
Python
def countSub(s):
    MOD = 10**9 + 7
    a = ab = abc = 0

    for ch in s:
        if ch == 'a':
            a = (2 * a + 1) % MOD
        elif ch == 'b':
            ab = (2 * ab + a) % MOD
        elif ch == 'c':
            abc = (2 * abc + ab) % MOD

    return abc

s = "abbc"
print(countSub(s))
C#
using System;

class GfG {
    static int CountSub(string s) {
        const int MOD = 1000000007;
        long a = 0, ab = 0, abc = 0;

        foreach (char ch in s) {
            if (ch == 'a') {
                a = (2 * a + 1) % MOD;
            } else if (ch == 'b') {
                ab = (2 * ab + a) % MOD;
            } else if (ch == 'c') {
                abc = (2 * abc + ab) % MOD;
            }
        }

        return (int) abc;
    }

    static void Main() {
        string s = "abbc";

        Console.WriteLine(CountSub(s));
    }
}
JavaScript
function countSub(s) {
    const MOD = 1000000007n;
    let a = 0n, ab = 0n, abc = 0n;

    for (const ch of s) {
        if (ch === 'a') {
            a = (2n * a + 1n) % MOD;
        } else if (ch === 'b') {
            ab = (2n * ab + a) % MOD;
        } else if (ch === 'c') {
            abc = (2n * abc + ab) % MOD;
        }
    }

    return Number(abc);
}

// Driver Code
const s = "abbc";
console.log(countSub(s));

Output
3
Comment