greedy
dp
binary search
two pointers
]
Leetcode 0392. Is Subsequence
https://leetcode.com/problems/is-subsequence
Solution 1: dynamic programming
The first solution I think when I see this problem is dynamic programming. Let dp[i][j] = 1 if s[:j] is substring of t[:i]. How can we find it:
- If
s[j] == t[i], then we need to search strings[:j-1]int[:i-1] - If
s[j] != t[i], then we need to search strings[:j]int[:i-1]
Here we use also s = "!" + s trick which allows as to handle border cases with empty strings.
Complexity: time and space complexity is O(nm), because we need to iterate over all table once.
class Solution:
def isSubsequence(self, s, t):
s, t = "!" + s, "!" + t
m, n = len(s), len(t)
dp = [[0] * m for _ in range(n)]
for i in range(n): dp[i][0] = 1
for i,j in product(range(1, n), range(1, m)):
if s[j] == t[i]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = dp[i-1][j]
return dp[-1][-1]
Solution 2: two pointers
You can notice, that actually we update only one element in each row of or dp table, so we do a lot of job which is not neccesary. Each moment of time we need to keep only two pointers: one for stirng s and one for string t. Then if we found new symbol in string t, which is equal to symbol in s, we move two pointers by one. If we did not found, then we move onle pointer for t.
Complexity is now only O(n + m) = O(n), because we traverse both strings only once.
class Solution:
def isSubsequence(self, s, t):
s_i, t_i = 0, 0
while s_i < len(s) and t_i < len(t):
s_i, t_i = s_i + (s[s_i] == t[t_i]), t_i + 1
return s_i == len(s)
Solution 3: binary search for follow-up question.
If we have a lot strings S1, S2, ... , Sk, where k is big number we want to find faster method. Let us create for each symbol sorted list of indexes for this symbol.
Complexity, both time and space of preprocessing is O(n), we iterate once over our list.
Complexity of one search for string S_i is O(m_i)*log(n), where m_i is length of string and we have log(n) factor, because we potentially can have list of indexes with length n. So if m is the longest length of S_i, we have complexity O(k m log n), when two-pointer approach has O(k n) complexity. So, if length of original string n is big and m is small, it is worth to use this method.
class Solution:
def isSubsequence(self, s, t):
places = defaultdict(list)
for i, symbol in enumerate(t):
places[symbol].append(i)
current_place = 0
for symbol in s:
current_ind = bisect.bisect_left(places[symbol], current_place)
if current_ind >= len(places[symbol]):
return False
current_place = places[symbol][current_ind] + 1
return True
If you like the solution, you can upvote it on leetcode discussion section: Problem 0392