AlgoMaster Logo

Is Subsequence

easyFrequency8 min readUpdated June 23, 2026

Understanding the Problem

We need to check whether every character of s appears in t, in the same order, but not necessarily consecutively. We can skip characters in t, but we cannot rearrange them. For example, "ace" is a subsequence of "abcde" because a, c, and e appear in that order, while "aec" is not, since e comes before c in the target.

The follow-up changes the problem. Checking one string against t is a single linear scan. But if we need to check a billion strings against the same fixed t, re-scanning t for each one is wasteful. Preprocessing t once lets each query do less work.

Key Constraints:

  • 0 <= s.length <= 100 and 0 <= t.length <= 10^4 → both strings fit comfortably in memory, so the single-query solution has no performance pressure. The cost that matters is per-query work when there are many queries.
  • lowercase English letters only → there are 26 possible characters. This bounds the size of any per-character lookup structure, which is what makes the follow-up approaches practical.
  • s can be empty → an empty s is a subsequence of any t, including an empty one. The pointer and loop conditions below handle this without a special case.

Approach 1: Two Pointers

Intuition

Walk through both strings with one pointer for s and one for t. When the current characters match, advance the pointer on s. Advance the pointer on t on every step. If the s pointer reaches the end before t runs out, every character of s was matched in order, so s is a subsequence.

The strategy is greedy: whenever a character of t matches the character of s we are looking for, we consume it immediately rather than saving it for later. This is safe because matching the earliest possible occurrence in t leaves the longest remaining suffix of t to match the rest of s. Any valid matching that skips this occurrence and uses a later one can be rewritten to use the earlier occurrence instead, so the greedy choice never loses a solution that a delayed choice would have found.

Algorithm

  1. Initialize two pointers: i = 0 for s, j = 0 for t.
  2. While both pointers are within bounds:
    • If s[i] == t[j], we've matched a character. Advance both i and j.
    • Otherwise, advance only j (skip this character in t).
  3. After the loop, if i == s.length, all characters of s were matched. Return true. Otherwise, return false.

Example Walkthrough

1Initialize: s="abc", i=0, j=0. Looking for 'a'
0
i→'a'
a
j
1
h
2
b
3
g
4
d
5
c
1/6

Code

For a single query this is optimal: each character of both strings is examined at most once. The limitation appears under the follow-up, where many queries run against the same t. Each query re-scans t from the start. The next approach preprocesses t once so that each query can locate matches by binary search instead of a full scan.

Approach 2: Binary Search with Preprocessing

Intuition

When t is fixed and many query strings run against it, we can preprocess t once and reuse the result across all queries. For each character of the alphabet, store the sorted list of indices where that character appears in t. Then, for each character of s, find the next valid position with a binary search over that character's index list instead of scanning t.

For example, if t = "ahbgdc", we'd build this index map:

  • 'a' → [0]
  • 'b' → [2]
  • 'c' → [5]
  • 'd' → [4]
  • 'g' → [3]
  • 'h' → [1]

To check if s = "abc" is a subsequence, we start at position -1 (before the string). For 'a', binary search for the smallest index > -1 in [0]. That gives us 0. For 'b', search for the smallest index > 0 in [2]. That gives us 2. For 'c', search for the smallest index > 2 in [5]. That gives us 5. We found valid positions for all characters, so it's a subsequence.

Algorithm

  1. Preprocess: Build a hash map where each key is a character, and the value is a sorted list of indices where that character appears in t.
  2. For each query string s:
    • Initialize prevIndex = -1 (the position of the last matched character).
    • For each character c in s:
      • Look up the index list for c. If the list doesn't exist, return false.
      • Binary search for the smallest index in the list that is greater than prevIndex.
      • If no such index exists, return false.
      • Otherwise, update prevIndex to this index.
    • If all characters are matched, return true.

Example Walkthrough

1Preprocessed index map: a→[0], h→[1], b→[2], g→[3], d→[4], c→[5]. prevIndex=-1
0
a
1
h
2
b
3
g
4
d
5
c
1/5

Code

Each query is O(n log m) because of the per-character binary search. The next approach removes the log factor by precomputing, for every position in t and every character, where the next occurrence of that character is, turning each lookup into a constant-time table read.

Approach 3: Dynamic Programming (Next Occurrence Matrix)

Intuition

Precompute a jump table for t: for every position j and every character c, store the index of the next occurrence of c at or after position j. Each character lookup during a query then becomes a constant-time table read.

The table is built by scanning t from right to left. The right-to-left order matters because the next occurrence of a character at position j depends only on what comes after j, which has already been computed. At position j, the next occurrence of t[j] is j itself. For every other character, the next occurrence at j is the same as at j + 1, so we copy row j + 1 and overwrite the single entry for t[j]. This fills the entire table in O(26 * m) time.

To check a query, start at position 0 in t. Look up the next occurrence of s[0], move one past it, look up the next occurrence of s[1] from there, and continue. Each lookup is O(1), so a query costs O(n).

Algorithm

  1. Preprocess: Build a 2D array next of size (m + 1) x 26.
    • Initialize the last row (position m) with -1 for all characters (no characters after the end).
    • Scan from j = m - 1 down to 0:
      • Copy row j + 1 into row j.
      • Set next[j][t[j] - 'a'] = j (this character is available at position j).
  2. For each query string s:
    • Start at position j = 0.
    • For each character c in s:
      • Look up next[j][c - 'a'].
      • If it's -1, return false (character not available).
      • Otherwise, set j = next[j][c - 'a'] + 1 (move past this matched character).
    • If all characters are matched, return true.

Example Walkthrough

1Build next occurrence matrix for t="ahbgdc" (right to left scan)
0
a
1
h
2
b
3
g
4
d
5
c
1/6

Code