392. Is Subsequence
Difficulty: EASY
Problem description can be found here.
Approach
Section titled “Approach”What is a Subsequence?
Section titled “What is a Subsequence?”A subsequence is a sequence that can be derived from another sequence by deleting some (or no) elements without changing the order of the remaining elements. Unlike substrings, subsequences don’t need to be contiguous.
For example:
"abc"is a subsequence of"ahbgdc"because we can find ‘a’ → ‘b’ → ‘c’ in order"axc"is NOT a subsequence of"ahbgdc"because there’s no ‘c’ after ‘x’
Why This Works
Section titled “Why This Works”This problem uses a two-pointer technique where both pointers move in the same direction through their respective strings. The key insight is:
- We maintain a pointer
sPointertracking our current position in strings(the subsequence we’re looking for) - We iterate through string
twithtPointer, looking for each character ofsin order - When we find a match, we advance
sPointerto look for the next character - At the end, if
sPointerhas reached the end ofs, all characters were found in order
This works because:
- We never go backwards in
t, preserving the order requirement - We only advance
sPointerwhen we find a match, ensuring all characters are found - A single pass through
tis sufficient (O(n) time)
Algorithm Steps
Section titled “Algorithm Steps”- Initialize
sPointer = 0to track position in strings - Iterate through each character in string
tusingtPointer - For each character in
t, check if it matches the current character we’re looking for ins(atsPointer) - If it matches, increment
sPointerto move to the next character ins - After iterating through all of
t, check ifsPointer === s.length - If true, all characters were found in order → return
true; otherwise returnfalse
Visualization
Section titled “Visualization”Let’s trace through s = "abc" and t = "ahbgdc":
t: a h b g d c ↑s: a b c (looking for: 'a')sPointer: 0Step 1: t[0] = 'a' matches s[0] = 'a' → advance sPointer
t: a h b g d c ↑s: a b c (looking for: 'b')sPointer: 1Step 2: t[1] = 'h' doesn’t match s[1] = 'b' → no advance
t: a h b g d c ↑s: a b c (looking for: 'b')sPointer: 1Step 3: t[2] = 'b' matches s[1] = 'b' → advance sPointer
t: a h b g d c ↑s: a b c (looking for: 'c')sPointer: 2Step 4: t[3] = 'g' doesn’t match s[2] = 'c' → no advance
t: a h b g d c ↑s: a b c (looking for: 'c')sPointer: 2Step 5: t[4] = 'd' doesn’t match s[2] = 'c' → no advance
t: a h b g d c ↑s: a b c (looking for: 'c')sPointer: 2Step 6: t[5] = 'c' matches s[2] = 'c' → advance sPointer
t: a h b g d c ↑s: a b c (all found!)sPointer: 3Final check: sPointer (3) === s.length (3) → Return TRUE
Now let’s trace a failing case: s = "axc" and t = "ahbgdc"
t: a h b g d c
s: a x c (looking for: 'a')sPointer: 0Step 1: t[0] = 'a' matches s[0] = 'a' → advance sPointer
t: a h b g d c ↑s: a x c (looking for: 'x')sPointer: 1Step 2-5: Continue scanning, but never find ‘x’ in t
t: a h b g d c ↑s: a x c (looking for: 'x')sPointer: 1 (stuck!)Step 6: t[5] = 'c' doesn’t match s[1] = 'x'
Final check: sPointer (1) !== s.length (3) → Return FALSE
Key Pattern / Code Template
Section titled “Key Pattern / Code Template”function isSubsequence(s: string, t: string): boolean { let sPointer = 0;
for (let tPointer = 0; tPointer < t.length; tPointer++) { if (t[tPointer] === s[sPointer]) { sPointer++; } }
return sPointer === s.length;}Template breakdown:
sPointer: Tracks position in the target subsequence- Single for-loop: Iterates through
tonce (O(n)) - Match check: Only advances when characters match
- Final check: Verifies all characters were found
When to Use This Pattern
Section titled “When to Use This Pattern”Use the two-pointers subsequence pattern when:
- Checking subsequence existence - Determining if one string is a subsequence of another
- Pattern matching in order - Finding elements in a sequence while preserving order
- String matching with gaps - When you need to find characters that appear in order but not necessarily contiguously
- Verification problems - Checking if one sequence can be derived from another by deleting elements
Related problems:
- 521. Longest Uncommon Subsequence I
-
- Longest Uncommon Subsequence II
-
- Number of Matching Subsequences
-
- Matrix Diagonal Sum (different concept, but similar pattern)
Solution
Section titled “Solution”function isSubsequence(s: string, t: string): boolean { const sArray = s.split(""); const tArray = t.split("");
let sPointer = 0;
for (let tPointer = 0; tPointer < tArray.length; tPointer++) { if (tArray[tPointer] === sArray[sPointer]) { sPointer++; } }
return sPointer === sArray.length;}
isSubsequence("abc", "ahbgdc"); // trueisSubsequence("axc", "ahbgdc"); // false