Find the number of distinct strings of length n that can be formed using only the characters 'R', 'B', and 'G', such that the string contains at least r occurrences of 'R', at least b occurrences of 'B', and at least g occurrences of 'G'.
Examples:
Input: n = 4, r = 1, b = 1, g = 1
Output: 36
Explanation: With at least one of each character required across a length-4 string, exactly one character is "extra" and can be any of 'R', 'B', or 'G'. This gives three possible character sets: {R,R,B,G}, {R,B,B,G}, and {R,B,G,G}. Each set has 4!/2! = 12 distinct arrangements (since one character repeats), giving 3 × 12 = 36 total strings.Input: n = 4, r = 2, b = 1, g = 0
Output: 22
Explanation: The valid character distributions, satisfying at least 2 'R', at least 1 'B', and at least 0 'G', are: {R,R,B,G} with 12 arrangements, {R,R,B,B} with 6 arrangements, and {R,R,R,B} with 4 arrangements. Total: 12 + 6 + 4 = 22.
Table of Content
[Naive Approach] Using Recursive Generation - O((3^n)*n) Time and O(n) Space
The idea is to recursively build every possible string of length n by choosing 'R', 'B', or 'G' at each position, then check if the final string has enough of each character.
Step by Step Implementation:
- At each position, try placing 'R', 'B', and 'G', one at a time.
- Recurse to the next position, tracking how many of each character have been placed so far.
- Once all n positions are filled, check if the counts meet the required minimums.
- Count it as valid if they do.
#include <iostream>
using namespace std;
int generate(int pos, int n, int rc, int bc, int gc, int r, int b, int g) {
// All positions filled -- check if the counts meet the minimums
if (pos == n) return (rc >= r && bc >= b && gc >= g) ? 1 : 0;
// Try placing 'R', 'B', and 'G' at this position, sum up valid results
return generate(pos + 1, n, rc + 1, bc, gc, r, b, g)
+ generate(pos + 1, n, rc, bc + 1, gc, r, b, g)
+ generate(pos + 1, n, rc, bc, gc + 1, r, b, g);
}
int countStrings(int n, int r, int b, int g) {
return generate(0, n, 0, 0, 0, r, b, g);
}
int main() {
int n = 4, r = 1, b = 1, g = 1;
cout << countStrings(n, r, b, g) << endl;
return 0;
}
class GFG {
static int generate(int pos, int n, int rc, int bc, int gc, int r, int b, int g) {
// All positions filled -- check if the counts meet the minimums
if (pos == n) return (rc >= r && bc >= b && gc >= g) ? 1 : 0;
// Try placing 'R', 'B', and 'G' at this position, sum up valid results
return generate(pos + 1, n, rc + 1, bc, gc, r, b, g)
+ generate(pos + 1, n, rc, bc + 1, gc, r, b, g)
+ generate(pos + 1, n, rc, bc, gc + 1, r, b, g);
}
static int countStrings(int n, int r, int b, int g) {
return generate(0, n, 0, 0, 0, r, b, g);
}
public static void main(String[] args) {
int n = 4, r = 1, b = 1, g = 1;
System.out.println(countStrings(n, r, b, g));
}
}
def generate(pos, n, rc, bc, gc, r, b, g):
# All positions filled -- check if the counts meet the minimums
if pos == n:
return 1 if (rc >= r and bc >= b and gc >= g) else 0
# Try placing 'R', 'B', and 'G' at this position, sum up valid results
return (generate(pos + 1, n, rc + 1, bc, gc, r, b, g)
+ generate(pos + 1, n, rc, bc + 1, gc, r, b, g)
+ generate(pos + 1, n, rc, bc, gc + 1, r, b, g))
def countStrings(n, r, b, g):
return generate(0, n, 0, 0, 0, r, b, g)
n, r, b, g = 4, 1, 1, 1
print(countStrings(n, r, b, g))
using System;
class GFG {
static int Generate(int pos, int n, int rc, int bc, int gc, int r, int b, int g) {
// All positions filled -- check if the counts meet the minimums
if (pos == n) return (rc >= r && bc >= b && gc >= g) ? 1 : 0;
// Try placing 'R', 'B', and 'G' at this position, sum up valid results
return Generate(pos + 1, n, rc + 1, bc, gc, r, b, g)
+ Generate(pos + 1, n, rc, bc + 1, gc, r, b, g)
+ Generate(pos + 1, n, rc, bc, gc + 1, r, b, g);
}
static int countStrings(int n, int r, int b, int g) {
return Generate(0, n, 0, 0, 0, r, b, g);
}
static void Main() {
int n = 4, r = 1, b = 1, g = 1;
Console.WriteLine(countStrings(n, r, b, g));
}
}
function generate(pos, n, rc, bc, gc, r, b, g) {
// All positions filled -- check if the counts meet the minimums
if (pos === n) return (rc >= r && bc >= b && gc >= g) ? 1 : 0;
// Try placing 'R', 'B', and 'G' at this position, sum up valid results
return generate(pos + 1, n, rc + 1, bc, gc, r, b, g)
+ generate(pos + 1, n, rc, bc + 1, gc, r, b, g)
+ generate(pos + 1, n, rc, bc, gc + 1, r, b, g);
}
function countStrings(n, r, b, g) {
return generate(0, n, 0, 0, 0, r, b, g);
}
// Driver Code
const n = 4, r = 1, b = 1, g = 1;
console.log(countStrings(n, r, b, g));
Output
36
[Expected Approach] Using Formula and Precomputed Factorials - O(n*n) Time and O(n) Space
The idea is to directly count valid character distributions instead of generating every string. For every way to split n positions into x R's, y B's, and z G's (with x ≥ r, y ≥ b, z ≥ g), the number of strings with that exact distribution is the coefficient n! / (x! × y! × z!). Precomputing factorials lets each coefficient be computed in O(1).
Step by Step Implementation:
- Precompute factorials from 0! to n!.
- Try every valid count of R's (x, from r to n) and B's (y, from b, as long as x+y ≤ n).
- Compute z = n - x - y, and check if z ≥ g.
- If valid, add n! / (x! × y! × z!) to the total.
- Return the total.
#include <iostream>
#include <vector>
using namespace std;
int countStrings(int n, int r, int b, int g) {
// Precompute factorials 0! through n!
vector<long long> fact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
// Try every valid split (x, y, z) with x>=r, y>=b, z>=g, x+y+z=n
long long total = 0;
for (int x = r; x <= n; x++) {
for (int y = b; x + y <= n; y++) {
int z = n - x - y;
if (z >= g) {
total += fact[n] / (fact[x] * fact[y] * fact[z]);
}
}
}
return (int)total;
}
int main() {
int n = 4, r = 1, b = 1, g = 1;
cout << countStrings(n, r, b, g) << endl;
return 0;
}
class GFG {
static int countStrings(int n, int r, int b, int g) {
// Precompute factorials 0! through n!
long[] fact = new long[n + 1];
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
// Try every valid split (x, y, z) with x>=r, y>=b, z>=g, x+y+z=n
long total = 0;
for (int x = r; x <= n; x++) {
for (int y = b; x + y <= n; y++) {
int z = n - x - y;
if (z >= g) {
total += fact[n] / (fact[x] * fact[y] * fact[z]);
}
}
}
return (int) total;
}
public static void main(String[] args) {
int n = 4, r = 1, b = 1, g = 1;
System.out.println(countStrings(n, r, b, g));
}
}
def countStrings(n, r, b, g):
# Precompute factorials 0! through n!
fact = [1] * (n + 1)
for i in range(1, n + 1):
fact[i] = fact[i - 1] * i
# Try every valid split (x, y, z) with x>=r, y>=b, z>=g, x+y+z=n
total = 0
for x in range(r, n + 1):
y = b
while x + y <= n:
z = n - x - y
if z >= g:
total += fact[n] // (fact[x] * fact[y] * fact[z])
y += 1
return total
n, r, b, g = 4, 1, 1, 1
print(countStrings(n, r, b, g))
using System;
class GFG {
static int countStrings(int n, int r, int b, int g) {
// Precompute factorials 0! through n!
long[] fact = new long[n + 1];
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
// Try every valid split (x, y, z) with x>=r, y>=b, z>=g, x+y+z=n
long total = 0;
for (int x = r; x <= n; x++) {
for (int y = b; x + y <= n; y++) {
int z = n - x - y;
if (z >= g) {
total += fact[n] / (fact[x] * fact[y] * fact[z]);
}
}
}
return (int)total;
}
static void Main() {
int n = 4, r = 1, b = 1, g = 1;
Console.WriteLine(countStrings(n, r, b, g));
}
}
function countStrings(n, r, b, g) {
// Precompute factorials 0! through n!
const fact = new Array(n + 1).fill(1);
for (let i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
// Try every valid split (x, y, z) with x>=r, y>=b, z>=g, x+y+z=n
let total = 0;
for (let x = r; x <= n; x++) {
for (let y = b; x + y <= n; y++) {
const z = n - x - y;
if (z >= g) {
total += fact[n] / (fact[x] * fact[y] * fact[z]);
}
}
}
return total;
}
// Driver Code
const n = 4, r = 1, b = 1, g = 1;
console.log(countStrings(n, r, b, g));
Output
36