392. Is Subsequence
Problem Description
The problem presents us with two strings, s
and t
. The task is to determine whether s
is a subsequence of t
. A subsequence of a string is formed by removing zero or more characters from the original string without changing the order of the remaining characters. The function should return true
if s
can be obtained from t
by such a deletion process, otherwise false
.
For example, if s
is "ace" and t
is "abcde", then s
is a subsequence of t
because you can delete characters 'b' and 'd' from "abcde" without changing the order of the remaining characters to get "ace".
Intuition
To determine if s
is a subsequence of t
, we iterate through both strings concurrently from the beginning, using pointers. If a character in s
matches a character in t
, we move forward in both strings. If the characters don't match, we only move forward in t
, since we're looking to delete or skip characters from t
. When the end of s
is reached, it signifies that all characters of s
have been matched in t
in order, proving that s
is a subsequence of t
.
We arrive at this solution approach because it efficiently traverses the strings without the need for backtracking. This approach leverages the fact that the relative order of characters in the subsequence must remain consistent with the larger string.
Learn more about Two Pointers and Dynamic Programming patterns.
Solution Approach
The solution provided uses a simple yet effective algorithm: the two-pointer technique. It involves iterating over both strings s
and t
simultaneously but independently, with pointers i
for string s
and j
for string t
.
Here is how the algorithm is implemented step by step:
- Initialize two variables,
i
andj
, to zero. These will act as pointers to the current character being compared in stringss
andt
, respectively. - Use a
while
loop to continue the iteration as long asi
is less than the length of strings
andj
is less than the length of stringt
. This helps in making sure we do not go out of bounds of either string. - Inside the
while
loop, compare the characters at the current pointers. Ifs[i]
equalst[j]
, this means the current character ins
is matched int
, and we move the pointeri
to the next position (i += 1
). - Regardless of whether we found a match or not, move the pointer
j
to the next position (j += 1
). This is because even if the current characters do not match, we want to continue looking for the next character ofs
in the remainder oft
. - After the loop, if
i
is equal to the length of strings
, it indicates that all characters ofs
have successfully been found int
in the correct order, and the function returnstrue
. - If the loop ends because
j
reaches the end oft
buti
has not yet reached the end ofs
, the function returnsfalse
because not all characters froms
could be matched int
.
By using this approach, the solution is efficient and eliminates the need for extra data structures, making the space complexity O(1) (since the pointers use a constant amount of extra space) and time complexity O(n), where n
is the length of string t
.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach. Suppose we have two strings, s
is "axc" and t
is "ahbgdc".
- Initialize pointers
i
andj
to 0, representing the starting positions ins
andt
. - Enter the
while
loop sincei < len(s)
andj < len(t)
. - Check if
s[i]
is equal tot[j]
(compare 'a' with 'a'):- Since they match, increment
i
to 1 (nows[i]
is 'x') and incrementj
to 1 (nowt[j]
is 'h').
- Since they match, increment
s[i]
is 'x' andt[j]
is 'h', they do not match:- Increment only
j
to 2 (t[j]
is now 'b').
- Increment only
- Continue checking:
s[i]
is 'x' andt[j]
is 'b' (no match), incrementj
to 3s[i]
is 'x' andt[j]
is 'g' (no match), incrementj
to 4s[i]
is 'x' andt[j]
is 'd' (no match), incrementj
to 5s[i]
is 'x' andt[j]
is 'c' (no match), since'x'
is not found andj
has reached the end oft
, we exit the loop.
- After exiting the loop, we check if
i
is equal tolen(s)
which in this case it is not (i
is 1 andlen(s)
is 3). - Since
i != len(s)
, we can conclude that not all characters ofs
were matched int
, and therefore returnfalse
.
Throughout this example, we were able to test whether s
is a subsequence of t
without needing any extra data structures and by only incrementing the pointers as needed.
Solution Implementation
1class Solution:
2 def is_subsequence(self, subsequence: str, sequence: str) -> bool:
3 # Initialize two pointers for both the subsequence and the sequence
4 subsequence_index = 0
5 sequence_index = 0
6
7 # Iterate over the sequence while there are characters left in both
8 # the subsequence and the sequence
9 while subsequence_index < len(subsequence) and sequence_index < len(sequence):
10 # If the current characters match, move to the next character in the subsequence
11 if subsequence[subsequence_index] == sequence[sequence_index]:
12 subsequence_index += 1
13 # Move to the next character in the sequence
14 sequence_index += 1
15
16 # Return True if all characters in the subsequence have been matched
17 # This is indicated by subsequence_index pointing to the end of subsequence
18 return subsequence_index == len(subsequence)
19
1class Solution {
2 public boolean isSubsequence(String s, String t) {
3 // Lengths of the strings s and t
4 int lengthS = s.length(), lengthT = t.length();
5
6 // Initialize pointers for both the strings
7 int indexS = 0, indexT = 0;
8
9 // Iterate over both strings
10 while (indexS < lengthS && indexT < lengthT) {
11 // Check if the current character of s matches the current character of t
12 if (s.charAt(indexS) == t.charAt(indexT)) {
13 // If they match, move the pointer of s forward
14 ++indexS;
15 }
16 // Move the pointer of t forward
17 ++indexT;
18 }
19
20 // If indexS is equal to the length of s, all characters of s are found in t in sequence
21 // Therefore, s is a subsequence of t
22 return indexS == lengthS;
23 }
24}
25
1class Solution {
2public:
3 // The function checks if 's' is a subsequence of 't'.
4 bool isSubsequence(string s, string t) {
5 int sLength = s.size(), tLength = t.size(); // Store lengths of both strings.
6 int sIndex = 0, tIndex = 0; // Initialize indices for both strings.
7
8 // Loop through both strings.
9 for (; sIndex < sLength && tIndex < tLength; ++tIndex) {
10 // If characters match, move to the next character in 's'.
11 if (s[sIndex] == t[tIndex]) {
12 ++sIndex;
13 }
14 }
15
16 // If we have gone through the entire 's' string, it is a subsequence of 't'.
17 return sIndex == sLength;
18 }
19};
20
1function isSubsequence(subString: string, mainString: string): boolean {
2 const subStringLength = subString.length; // Store the length of the subsequence string
3 const mainStringLength = mainString.length; // Store the length of the main sequence string
4 let subStringIndex = 0; // Initialize an index to track the current position in subString
5
6 // Loop over the mainString while there are characters left in both strings
7 for (let mainStringIndex = 0; subStringIndex < subStringLength && mainStringIndex < mainStringLength; ++mainStringIndex) {
8 // If characters match, increment the index of the subString
9 if (subString[subStringIndex] === mainString[mainStringIndex]) {
10 ++subStringIndex;
11 }
12 }
13
14 // If subStringIndex equals to subStringLength, all characters of subString were found in order in mainString
15 return subStringIndex === subStringLength;
16}
17
Time and Space Complexity
The time complexity of the code is O(n)
where n
is the length of string t
. This is because the function iterates through each character of t
at most once. The pointer i
moves only when there is a match in s
and t
, which does not change the overall linear time complexity with respect to t
. It does not iterate more than n
times even if every character of s
is found in t
.
The space complexity of the code is O(1)
since no additional space is used that grows with the input size. The only extra memory used is for the two pointers i
and j
, which use a constant amount of space.
Learn more about how to find time and space complexity quickly using problem constraints.
Which data structure is used to implement recursion?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Want a Structured Path to Master System Design Too? Don’t Miss This!