Distinct Subsequences

Hard
#0115Time: O(2^m)Space: O(m + n)6 companies
Data structures

Prompt

Given two strings s and t, return the number of distinct subsequences of s which equals t.

The test cases are generated so that the answer fits on a 32-bit signed integer.

 

Example 1:

<strong><u>rabb</u></strong>b<strong><u>it</u></strong>

Example 2:

<strong><u>ba</u></strong>b<u><strong>g</strong></u>bag

 

Constraints:

  • 1 <= s.length, t.length <= 1000
  • s and t consist of English letters.

Approaches

4 approaches with complexity analysis and trade-offs.

This approach uses a simple recursive function to solve the problem. The function solve(i, j) calculates the number of distinct subsequences of s starting from index i that match t starting from index j.

Algorithm

  • Define a recursive function solve(s, t, i, j).
  • Base Case 1: If j equals the length of t, it means we have successfully formed t. Return 1.
  • Base Case 2: If i equals the length of s but j is less than the length of t, it's impossible to form t. Return 0.
  • If s.charAt(i) is equal to t.charAt(j), the result is the sum of two recursive calls: solve(s, t, i + 1, j + 1) (matching the characters) and solve(s, t, i + 1, j) (skipping s.charAt(i)).
  • If s.charAt(i) is not equal to t.charAt(j), we must skip s.charAt(i). The result is solve(s, t, i + 1, j).

Walkthrough

The core idea is to explore all possibilities. For each character s[i] in the source string s, we compare it with the character t[j] in the target string t.

  • If s[i] matches t[j], we have two choices:

    1. We can use s[i] to form the subsequence. In this case, we need to find the number of ways to form the rest of t (i.e., t[j+1:]) from the rest of s (i.e., s[i+1:]).
    2. We can skip s[i] and try to find a match for t[j] later in s. In this case, we need to find the number of ways to form t[j:] from s[i+1:]. The total number of ways is the sum of these two possibilities.
  • If s[i] does not match t[j], we have no choice but to skip s[i] and try to find a match for t[j] in the rest of s (i.e., s[i+1:]).

This logic is implemented recursively. The base cases for the recursion are:

  1. If we have successfully matched all characters of t (i.e., j reaches the end of t), we have found one valid subsequence. We return 1.
  2. If we have run out of characters in s (i.e., i reaches the end of s) but still have characters in t to match, it's impossible to form the subsequence. We return 0.
class Solution {    public int numDistinct(String s, String t) {        return solve(s, t, 0, 0);    }     private int solve(String s, String t, int i, int j) {        // Base case 1: If we have found all characters of t, it's a valid subsequence.        if (j == t.length()) {            return 1;        }        // Base case 2: If we have run out of characters in s but not in t.        if (i == s.length()) {            return 0;        }         // If characters match, we have two options:        // 1. Match s[i] with t[j] and look for the rest of t in the rest of s.        // 2. Skip s[i] and look for t[j] in the rest of s.        if (s.charAt(i) == t.charAt(j)) {            return solve(s, t, i + 1, j + 1) + solve(s, t, i + 1, j);        } else {            // If characters don't match, we must skip s[i].            return solve(s, t, i + 1, j);        }    }}

Complexity

Time

O(2^m)

Space

O(m + n)

Trade-offs

Pros

  • Simple and easy to understand the logic.

  • Direct translation of the problem's recursive definition.

Cons

  • Extremely inefficient due to a large number of overlapping subproblems.

  • Will result in a 'Time Limit Exceeded' (TLE) error for larger inputs.

Solutions

class Solution {public  int numDistinct(String s, String t) {    int n = t.length();    int[] f = new int[n + 1];    f[0] = 1;    for (char a : s.toCharArray()) {      for (int j = n; j > 0; --j) {        char b = t.charAt(j - 1);        if (a == b) {          f[j] += f[j - 1];        }      }    }    return f[n];  }}

Video walkthrough

Newsletter

One sharp idea, every week

System design and interview prep — short enough to finish.

No spam. Unsubscribe anytime.

Practice

Same difficulty — related problems to reinforce the pattern.