Count Occurrences as a Subsequence (original) (raw)

Given two strings **s1 and **s2, count the number of subsequences of string **s1 equal to string **s2. Return the total count modulo **1e9+7.

**Examples:

**Input: s1 = "geeksforgeeks", s2 = "gks"
**Output: 4
**Explanation: We can pick characters from s1 as a subsequence from indices [0, 3, 4], [0, 3, 12], [0, 11, 12] and [8, 11, 12]. So total 4 subsequences of s1 that are equal to s2.

**Input: s1 = "problemoftheday", s2 = "geek"
**Output: 0
**Explanation: No subsequence of string s1 is equal to string s2.

Table of Content

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

The idea is to recursively process the characters of s1. For each character, either skip it or include it if it matches the current character of s2. Count all possible ways that successfully match every character of s2.

C++ `

#include using namespace std;

static const int MOD = 1e9 + 7;

int solve(string &s1, string &s2, int i, int j) { // All characters of s2 are matched if (j == s2.size()) { return 1; }

// s1 is exhausted before matching s2
if (i == s1.size())
{
    return 0;
}

// Ignore current character of s1
long long ans = solve(s1, s2, i + 1, j);

// If characters match, consider current character
if (s1[i] == s2[j])
{
    ans += solve(s1, s2, i + 1, j + 1);
}

return ans % MOD;

}

int countWays(string &s1, string &s2) { return solve(s1, s2, 0, 0); }

// Driver Code int main() { string s1 = "geeksforgeeks"; string s2 = "gks";

cout << countWays(s1, s2);

return 0;

}

Java

import java.util.*;

public class GfG { static final int MOD = 1000000007;

static int solve(String s1, String s2, int i, int j)
{
    // All characters of s2 are matched
    if (j == s2.length()) {
        return 1;
    }

    // s1 is exhausted before matching s2
    if (i == s1.length()) {
        return 0;
    }

    // Ignore current character of s1
    long ans = solve(s1, s2, i + 1, j);

    // If characters match, consider current character
    if (s1.charAt(i) == s2.charAt(j)) {
        ans += solve(s1, s2, i + 1, j + 1);
    }

    return (int)(ans % MOD);
}

static int countWays(String s1, String s2)
{
    return solve(s1, s2, 0, 0);
}

// Driver Code
public static void main(String[] args)
{
    String s1 = "geeksforgeeks";
    String s2 = "gks";

    System.out.println(countWays(s1, s2));
}

}

Python

MOD = 10**9 + 7

def solve(s1, s2, i, j): # All characters of s2 are matched if j == len(s2): return 1

# s1 is exhausted before matching s2
if i == len(s1):
    return 0

# Ignore current character of s1
ans = solve(s1, s2, i + 1, j)

# If characters match, consider current character
if s1[i] == s2[j]:
    ans += solve(s1, s2, i + 1, j + 1)

return ans % MOD

def countWays(s1, s2): return solve(s1, s2, 0, 0)

Driver Code

Driver Code

if name == "main": s1 = "geeksforgeeks" s2 = "gks" print(countWays(s1, s2))

C#

using System;

public class GfG { static readonly int MOD = 1000000007;

static int Solve(string s1, string s2, int i, int j)
{
    if (j == s2.Length)
        return 1;

    if (i == s1.Length)
        return 0;

    long ans = Solve(s1, s2, i + 1, j);

    if (s1[i] == s2[j])
        ans += Solve(s1, s2, i + 1, j + 1);

    return (int)(ans % MOD);
}

public static void Main()
{
    string s1 = "geeksforgeeks";
    string s2 = "gks";

    Console.WriteLine(Solve(s1, s2, 0, 0));
}

}

JavaScript

function solve(s1, s2, i, j) { if (j === s2.length) return 1; if (i === s1.length) return 0;

let ans = solve(s1, s2, i + 1, j);

if (s1[i] === s2[j]) {
    ans += solve(s1, s2, i + 1, j + 1);
}

return ans;

}

function countWays(s1, s2) { const MOD = 1000000007; return solve(s1, s2, 0, 0) % MOD; }

// Driver Code let s1 = "geeksforgeeks"; let s2 = "gks";

console.log(countWays(s1, s2));

`

**Time Complexity: O(2^n)
**Auxiliary Space: O(n + m)

[Expected Approach - 1] Using Bottom-Up Dynamic Programming - (n * m) Time O(n * m) Space

The idea is to use Dynamic Programming where dp[i][j] stores the number of ways to form the first j characters of s2 using the first i characters of s1. For each character of s1:

The final answer is stored in dp[n][m].

**Working of the Approach:

#include #include using namespace std;

int countWays(string &s1, string &s2) { int n = s1.length(), m = s2.length();

// create a table to store result for sub-problems
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

for (int i = 0; i <= m; i++)
    dp[0][i] = 0;
for (int i = 0; i <= n; i++)
    dp[i][0] = 1;

int mod = 1e9 + 7;

// fill the table in bottom-up manner
for (int i = 1; i <= n; i++)
{
    for (int j = 1; j <= m; j++)
    {

        // if last characters are same, we have two
        // options -
        // 1. consider last characters of both strings
        //    in solution
        // 2. ignore last character of first string
        if (s1[i - 1] == s2[j - 1])
        {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
        }

        // If last character are different, ignore
        // last character of first string
        else
        {
            dp[i][j] = dp[i - 1][j] % mod;
        }
    }
}
return dp[n][m] % mod;

}

// Driver Code int main() { string s1 = "geeksforgeeks"; string s2 = "gks";

cout << countWays(s1, s2);

return 0;

}

C

#include <stdio.h> #include <string.h>

#define mod 1000000007

int countWays(char *s1, char *s2) { int n = strlen(s1), m = strlen(s2);

// create a table to store result for sub-problems
int dp[n + 1][m + 1];

for (int i = 0; i <= m; i++)
    dp[0][i] = 0;
for (int i = 0; i <= n; i++)
    dp[i][0] = 1;

// fill the table in bottom-up manner
for (int i = 1; i <= n; i++)
{
    for (int j = 1; j <= m; j++)
    {
        // if last characters are same, we have two
        // options -
        // 1. consider last characters of both strings
        //    in solution
        // 2. ignore last character of first string
        if (s1[i - 1] == s2[j - 1])
        {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod;
        }
        // If last character are different, ignore
        // last character of first string
        else
        {
            dp[i][j] = dp[i - 1][j] % mod;
        }
    }
}
return dp[n][m] % mod;

}

// Driver Code int main() { char s1[] = "geeksforgeeks"; char s2[] = "gks";

printf("%d", countWays(s1, s2));

return 0;

}

Java

public class GfG { static int countWays(String s1, String s2) { int n = s1.length(), m = s2.length();

    // create a table to store result for sub-problems
    int[][] dp = new int[n + 1][m + 1];

    for (int i = 0; i <= m; i++)
        dp[0][i] = 0;
    for (int i = 0; i <= n; i++)
        dp[i][0] = 1;

    int mod = 1000000007;

    // fill the table in bottom-up manner
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {

            // if last characters are same, we have two
            // options -
            // 1. consider last characters of both
            // strings
            //    in solution
            // 2. ignore last character of first string
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                dp[i][j]
                    = (dp[i - 1][j - 1] + dp[i - 1][j])
                      % mod;
            }

            // If last character are different, ignore
            // last character of first string
            else {
                dp[i][j] = dp[i - 1][j] % mod;
            }
        }
    }
    return dp[n][m] % mod;
}

public static void main(String[] args)
{
    String s1 = "geeksforgeeks";
    String s2 = "gks";

    System.out.println(countWays(s1, s2));
}

}

Python

def countWays(s1, s2): n = len(s1) m = len(s2)

# create a table to store result for sub-problems
dp = [[0] * (m + 1) for _ in range(n + 1)]

for i in range(m + 1):
    dp[0][i] = 0
for i in range(n + 1):
    dp[i][0] = 1

mod = 10**9 + 7

# fill the table in bottom-up manner
for i in range(1, n + 1):
    for j in range(1, m + 1):

        # if last characters are same, we have two
        # options -
        # 1. consider last characters of both strings
        #    in solution
        # 2. ignore last character of first string
        if s1[i - 1] == s2[j - 1]:
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod
        # If last character are different, ignore
        # last character of first string
        else:
            dp[i][j] = dp[i - 1][j] % mod
return dp[n][m] % mod

Driver Code

if name == "main": s1 = "geeksforgeeks" s2 = "gks" print(countWays(s1, s2))

C#

using System;

public class GfG { public static int CountWays(string s1, string s2) { int n = s1.Length, m = s2.Length;

    // create a table to store result for sub-problems
    int[, ] dp = new int[n + 1, m + 1];

    for (int i = 0; i <= m; i++)
        dp[0, i] = 0;
    for (int i = 0; i <= n; i++)
        dp[i, 0] = 1;

    int mod = 1000000007;

    // fill the table in bottom-up manner
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            // if last characters are same, we have two
            // options -
            // 1. consider last characters of both
            // strings
            //    in solution
            // 2. ignore last character of first string
            if (s1[i - 1] == s2[j - 1]) {
                dp[i, j]
                    = (dp[i - 1, j - 1] + dp[i - 1, j])
                      % mod;
            }
            // If last character are different, ignore
            // last character of first string
            else {
                dp[i, j] = dp[i - 1, j] % mod;
            }
        }
    }
    return dp[n, m] % mod;
}

public static void Main()
{
    string s1 = "geeksforgeeks";
    string s2 = "gks";

    Console.WriteLine(CountWays(s1, s2));
}

}

JavaScript

function countWays(s1, s2) { let n = s1.length, m = s2.length;

// create a table to store result for sub-problems
let dp = Array.from({length : n + 1},
                    () => Array(m + 1).fill(0));

for (let i = 0; i <= m; i++)
    dp[0][i] = 0;
for (let i = 0; i <= n; i++)
    dp[i][0] = 1;

let mod = 1000000007;

// fill the table in bottom-up manner
for (let i = 1; i <= n; i++) {
    for (let j = 1; j <= m; j++) {

        // if last characters are same, we have two
        // options -
        // 1. consider last characters of both strings
        //    in solution
        // 2. ignore last character of first string
        if (s1[i - 1] == s2[j - 1]) {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j])
                       % mod;
        }
        // If last character are different, ignore
        // last character of first string
        else {
            dp[i][j] = dp[i - 1][j] % mod;
        }
    }
}
return dp[n][m] % mod;

}

// Driver Code let s1 = "geeksforgeeks"; let s2 = "gks";

console.log(countWays(s1, s2));

`

**Time Complexity: O(n * m)
**Auxiliary Space: O(n * m)

[Expected Approach - 2] Using Dynamic Programming (Space Optimized) - O(n * m) Time and O(m) Space

The idea is to optimize the DP solution by using a single array instead of a 2D table. Here, dp[j] stores the number of ways to form the first j characters of s2. For each character of s1, we traverse s2 from right to left so that previous states are not overwritten. If the characters match, we either include the current character in the subsequence or skip it. This reduces the space requirement while maintaining the same time complexity.

**Working of the Approach:

#include #include using namespace std;

int countWays(string &s1, string &s2) { int n = s1.length(); int m = s2.length();

int mod = 1e9 + 7;

// dp[j] stores the number of ways to form
// first j characters of s2
vector<int> dp(m + 1, 0);

// Empty string can always be formed
dp[0] = 1;

for (int i = 1; i <= n; i++)
{

    // Traverse from right to left
    for (int j = m; j >= 1; j--)
    {

        // If current characters match,
        // include or exclude current character
        if (s1[i - 1] == s2[j - 1])
        {
            dp[j] = (dp[j] + dp[j - 1]) % mod;
        }
    }
}

return dp[m];

}

// Driver Code int main() { string s1 = "geeksforgeeks"; string s2 = "gks";

cout << countWays(s1, s2);

return 0;

}

C

#include <stdio.h> #include <string.h>

#define MOD 1000000007

int countWays(char *s1, char *s2) { int n = strlen(s1); int m = strlen(s2);

int dp[m + 1];

// manual initialization
for (int j = 0; j <= m; j++)
    dp[j] = 0;

dp[0] = 1;

for (int i = 1; i <= n; i++)
{
    for (int j = m; j >= 1; j--)
    {
        if (s1[i - 1] == s2[j - 1])
        {
            dp[j] = (dp[j] + dp[j - 1]) % MOD;
        }
    }
}

return dp[m];

}

// Driver Code int main() { char s1[] = "geeksforgeeks"; char s2[] = "gks";

printf("%d", countWays(s1, s2));

return 0;

}

Java

import java.util.*;

public class GfG { static int countWays(String s1, String s2) { int n = s1.length(); int m = s2.length();

    int mod = 1000000007;

    // dp[j] stores the number of ways to form
    // first j characters of s2
    int[] dp = new int[m + 1];

    // Empty string can always be formed
    dp[0] = 1;

    for (int i = 1; i <= n; i++) {

        // Traverse from right to left
        for (int j = m; j >= 1; j--) {

            // If current characters match,
            // include or exclude current character
            if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                dp[j] = (dp[j] + dp[j - 1]) % mod;
            }
        }
    }

    return dp[m];
}

// Driver Code
public static void main(String[] args)
{
    String s1 = "geeksforgeeks";
    String s2 = "gks";

    System.out.println(countWays(s1, s2));
}

}

Python

def countWays(s1, s2): n = len(s1) m = len(s2)

mod = 10**9 + 7

# dp[j] stores the number of ways to form
# first j characters of s2
dp = [0] * (m + 1)

# Empty string can always be formed
dp[0] = 1

for i in range(1, n + 1):

    # Traverse from right to left
    for j in range(m, 0, -1):

        # If current characters match,
        # include or exclude current character
        if s1[i - 1] == s2[j - 1]:
            dp[j] = (dp[j] + dp[j - 1]) % mod

return dp[m]

Driver Code

if name == "main": s1 = "geeksforgeeks" s2 = "gks" print(countWays(s1, s2))

C#

using System;

public class GfG { public static int countWays(string s1, string s2) { int n = s1.Length; int m = s2.Length; int mod = 1000000007;

    int[] dp = new int[m + 1];
    dp[0] = 1;

    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= 1; j--) {
            if (s1[i - 1] == s2[j - 1]) {
                dp[j] = (dp[j] + dp[j - 1]) % mod;
            }
        }
    }

    return dp[m];
}

public static void Main()
{
    string s1 = "geeksforgeeks";
    string s2 = "gks";

    Console.WriteLine(countWays(s1, s2));
}

}

JavaScript

function countWays(s1, s2) { let n = s1.length; let m = s2.length;

let mod = 10**9 + 7;

// dp[j] stores the number of ways to form
// first j characters of s2
let dp = new Array(m + 1).fill(0);

// Empty string can always be formed
dp[0] = 1;

for (let i = 1; i <= n; i++) {

    // Traverse from right to left
    for (let j = m; j >= 1; j--) {

        // If current characters match,
        // include or exclude current character
        if (s1[i - 1] === s2[j - 1]) {
            dp[j] = (dp[j] + dp[j - 1]) % mod;
        }
    }
}

return dp[m];

}

// Driver Code let s1 = "geeksforgeeks"; let s2 = "gks";

console.log(countWays(s1, s2));

`

**Time Complexity: O(n * m)
**Auxiliary Space: O(m)