Last Updated: February 7, 2026
Given two strings s and t, return true if s is a subsequence of t, or false otherwise.
A subsequence of a string is a new string that is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ace" is a subsequence of "abcde" while "aec" is not).
Input: s = "abc", t = "ahbgdc"
Output: true
Input: s = "axc", t = "ahbgdc"
Output: false
s and t consist only of lowercase English letters.Follow up: Suppose there are lots of incoming s, say s1, s2, ..., sk where k >= 109, and you want to check one by one to see if t has its subsequence. In this scenario, how would you change your code?
Before diving into solutions, let's make sure we understand what a subsequence is and how it differs from a substring.
The key difference: in a substring, characters must be adjacent; in a subsequence, they can be scattered but must maintain their relative order.
Let's visualize this with an example:
Another way to think about it: imagine you are reading through t from left to right, and you are trying to "check off" each character of s in order. If you can check off all characters of s by the time you finish reading t, then s is a subsequence of t.
The most natural way to solve this problem is to simulate the process of matching characters. We use two pointers: one to track our current position in s (the pattern we are looking for) and another to scan through t (the string we are searching in).
As we scan through t, whenever we find a character that matches the current character we are looking for in s, we advance our pointer in s. If we manage to match all characters in s before (or exactly when) we finish scanning t, then s is a subsequence.
i = 0 (for string s) and j = 0 (for string t)s[i] == t[j], we found a match, so increment ij to continue scanning ti == s.length(), we matched all characters in s, so return truefalseTime Complexity: O(n) where n is the length of t
t at most oncet (when s is not a subsequence or matches at the very end)Space Complexity: O(1)
This is the optimal solution for a single query. But what happens when we have many queries?
The follow-up question changes everything. If we have billions of strings s1, s2, ..., sk to check against a single t, the two-pointer approach gives us O(k * n) time where n is the length of t. Can we do better?
The key insight is that we are repeatedly traversing the same string t. If we preprocess t, we can answer each query faster.
Here is the idea: for each character in the alphabet, precompute all the positions where that character appears in t. Store these positions in sorted lists. Then, for each query string s, we use binary search to find valid positions.
For example, if t = "ahbgdc":
When checking if s = "abc" is a subsequence:
Why Binary Search?
For each character in s, we need to find the first occurrence in t that comes after our current position. Since the position lists are sorted, binary search gives us this in O(log n) time instead of O(n) with linear search.
ts:currentPos = -1 (we start before the string)c in s:ccurrentPoscurrentPos to this positionfalsetruePreprocessing Time: O(n) where n is the length of t
t once to build the position mapPreprocessing Space: O(n)
t contributes one position)Query Time: O(m * log n) where m is the length of query string s
s, we perform one binary searcht are the same)Total Time for k Queries: O(n + k m log n)