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
- [Expected Approach - 1] Using Bottom-Up Dynamic Programming - (n * m) Time O(n * m) Space
- [Expected Approach - 2] Using Dynamic Programming (Space Optimized) - O(n * m) Time and O(m) Space
[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 ofs2. Count all possible ways that successfully match every character ofs2.
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 % MODdef 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 firstjcharacters ofs2using the firsticharacters ofs1. For each character ofs1:
- If it matches the current character of
s2, either include it in the subsequence or skip it.- Otherwise, skip the current character of
s1.The final answer is stored in
dp[n][m].
**Working of the Approach:
- Create a DP table
dpwheredp[i][j]stores the number of ways to form the firstjcharacters ofs2using the firsticharacters ofs1. - Initialize
dp[i][0] = 1since an empty string can always be formed. - Initialize
dp[0][j] = 0forj > 0since a non-empty string cannot be formed from an empty string. - If the current characters match, add the ways obtained by including and excluding the current character.
- If the characters do not match, carry forward the count by skipping the current character of
s1. - After filling the table,
dp[n][m]gives the number of timess2occurs as a subsequence ins1. C++ `
#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] % modDriver 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 firstjcharacters ofs2. For each character ofs1, we traverses2from 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:
- Use a 1D DP array
dpwheredp[j]stores the number of ways to form the firstjcharacters ofs2. - Initialize
dp[0] = 1since an empty string can always be formed. - Traverse each character of
s1one by one. - For every character of
s1, traverses2from right to left to avoid overwriting values needed for future computations. - If the current characters match, update
dp[j]by adding the ways of forming the previous prefix, i.e.,dp[j - 1]. - After processing all characters,
dp[m]gives the number of timess2occurs as a subsequence ins1. C++ `
#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)