LeetCode #392 Easy

Is Subsequence

Given two strings s and t, determine whether s is a subsequence of t.

two-pointersstringgreedydynamic-programming
Open on LeetCode ↗
02

Intuition

💡

The trap is reading subsequence as substring. They differ by one word: a substring must be contiguous, a subsequence may skip characters as long as the relative order survives. So for s = 'abc' and t = 'ahbgdc' the answer is true even though a, b and c are scattered — and any solution that resets progress when it hits a mismatch will report false. That reset is the actual bug. Walk both strings with a pointer each and make them behave differently on purpose: j walks through t on every single step, no exceptions, while i advances into s only when it finds the character it is waiting for. A mismatch discards one character of t and costs you nothing you had already earned. Taking the leftmost match every time is also safe, not just convenient — an earlier match leaves strictly more of t available for the rest of s, so it can never lose to a later one. The invariant is that s[0:i] is always a subsequence of the part of t already consumed, and if i reaches the end of s you have your proof, no matter how much of t is left over.

03

Approach

1

Two pointers with asymmetric movement

Put i at the start of s and j at the start of t. The whole algorithm lives in the fact that these two move under different rules: j increments unconditionally on every iteration, i only when s[i] == t[j]. Writing them as a symmetric pair is what produces the substring solution by accident, so the asymmetry deserves to be stated out loud.

2

Consume greedily on a match

When the characters agree, advance both — you have matched one more character of s and used up one of t. When they disagree, advance only j, discarding that character of t while every match so far stays banked. Greedy is provably correct here: matching s[i] at the earliest possible position in t leaves the largest possible suffix for the characters still to come.

3

Decide on i, not j

The loop ends when either string runs out. Return whether i reached the end of s — that is the only question. Leftover characters in t are irrelevant and testing j instead is a classic wrong answer: 'abc' is a subsequence of 'abcdef' even though four characters of t go unused.

04

Solution & live demo

python
1class Solution:
2 def isSubsequence(self, s: str, t: str) -> bool:
3 i = j = 0
4 
5 while i < len(s) and j < len(t):
6 if s[i] == t[j]:
7 i += 1
8 j += 1
9 else:
10 j += 1
11 
12 # only i matters — leftover characters in t are fine
13 return i == len(s)
05

Edge cases

s is empty

The empty string is a subsequence of everything; i already equals len(s) so the loop never runs and the check returns true.

t is empty but s is not

The loop exits at once with i still at 0, so the answer is false — nothing can be matched against nothing.

s is longer than t

t runs out before i reaches the end of s and the result is false; a length comparison up front makes this O(1).

Many queries of s against one fixed t

The follow-up scenario. Precompute, for each position in t and each letter, the next occurrence of that letter — then each query costs O(len(s)) instead of O(len(t)).

06

Complexity

Time
O(n + m)
Space
O(1)
j visits each character of t at most once and never rewinds, so the scan is a single pass; only two integer pointers are stored.