Problem of The Day: Is Subsequence
Problem Statement
Intuition
My initial thought is to iterate through the characters of t
and check if each character matches the corresponding character in s
. If I find a match, I can then iterate through the remaining characters of both strings to see if the entire s
is present as a subsequence in t
.
Approach
I will iterate through each character of t
, and when I find a character that matches the first character of s
, I will initiate a nested loop to compare the subsequent characters of both strings. If I successfully match all characters of s
in order, then I can conclude that s
is a subsequence of t
.
Complexity
-
Time complexity: O(m * n), where m is the length of
s
and n is the length oft
. This is because in the worst case, I may need to traverse through all characters oft
for each character ins
. -
Space complexity: O(1) as I am not using any additional data structures that scale with the input size.
Code
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
if not s:
return True
for i, c in enumerate(t):
if s and c == s[0]:
idx_t = i
idx_s = 0
while idx_t < len(t):
if t[idx_t] == s[idx_s]:
idx_s += 1
idx_t += 1
if idx_s == len(s):
return True
return False
Another Approach: buckets
This approach uses a dictionary to create “buckets” for each character in the second string, t
. These buckets contain the indices where each character occurs in t
.
Then, it iterates through the characters of the first string, s
, in reverse order. For each character, it checks if there is a corresponding bucket in t
. If so, it pops the last index from that bucket and ensures it is in a valid order relative to the previous character’s index.
If the indices are not in the correct order or the character is not present in t
, the function returns False
. If all characters are processed successfully, it returns True
, indicating that s
is a subsequence of t
.
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
buckets = defaultdict(list)
for i, c in enumerate(t):
buckets[c].append(i)
idx = len(t)
for c in reversed(s):
if c not in buckets:
return False
while buckets and c in buckets and buckets[c]:
i = buckets[c].pop()
if i < idx:
idx = i
break
else:
return False
if not buckets[c]:
del buckets[c]
return True
Editorial Solution
Approach 2: Two-Pointers
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
LEFT_BOUND, RIGHT_BOUND = len(s), len(t)
p_left = p_right = 0
while p_left < LEFT_BOUND and p_right < RIGHT_BOUND:
# move both pointers or just the right pointer
if s[p_left] == t[p_right]:
p_left += 1
p_right += 1
return p_left == LEFT_BOUND