Date and Time: Jun 27, 2024, 11:34 (EST)
Link: https://leetcode.com/problems/regular-expression-matching/
First Solution
-
We first check base cases:
i. Ifi, jinsandpare both out of bound, return True since they are finished.
ii. If onlyjinpis out of bound, butiinsstill not finished, we should return False. -
We then check
matchby checking ifiofsin bound and eithers[i] == p[j]orp[j] == '.'. -
We check if the next element
j+1inpis*, we first check ifj + 1in bound and ifp[j+1] == '*'. If yes, we check two cases:
i.match == Truethen we move to next elementi+1ins.
ii.match == False, which means currentp[j]doesn't equal tos[i], so we skip two elementsdfs(i, j+2)to skip the currentp[j]and the*sign. -
If
p[j+1]is not*, but we have amatch, we can just proceed normally bydfs(i+1, j+1). -
We return
Falsefor any other cases of no match.
DP solution
In the solution, we first check two stopping conditions, which are the cases if i, j are both out of bound that means s, p are matched; if p is out of bound but s is not, we should return False.
Then, we step into the case to capture if s, p are matched or not by checking i < len(s) and (s[i] == p[j] or p[j] == '.'). Next, we consider the case when p has '*' and we check the two cases if we use the '*' or not, if we use it, we need to check match and dfs(i+1, j), else dfs(i, j+2). Once we use the '*' and there is a match, we can forward the i ptr by 1, otherwise we don't use it and move the j ptr to the next non'*' character. If p doesn't contain '*', we check match, if there is a match, we forward both i, j ptr. Otherwise, we return False since there is no matching.
The dP version just adds a cache, we cache each checking conditions above into the cache[i, j], and everytime we runt the dfs() we can check if the result is in cache already or not.
class Solution:
def isMatch(self, s: str, p: str) -> bool:
def dfs(i, j):
# Both matches
if i >= len(s) and j >= len(p):
return True
# p is out of bound, but s is not
if i < len(s) and j >= len(p):
return False
# When i is in bound, s='a', (p='a' or p='.')
match = i < len(s) and (s[i] == p[j] or p[j] == '.')
# Case of '*', use it or not
if (j + 1) < len(p) and p[j + 1] == '*':
# If matches then we move i to the next, keep j the same
# Otherwise, we move j to the next element (non '*')
return (match and dfs(i+1, j)) or dfs(i, j+2)
# If p doesn't have '*'
if match:
return dfs(i+1, j+1)
return False
return dfs(0, 0)class Solution:
def isMatch(self, s: str, p: str) -> bool:
dP = {}
def dfs(i, j):
# Check if in dP
if (i, j) in dP:
return dP[(i, j)]
# Check stopping condtions
# Both matches
if i >= len(s) and j >= len(p):
return True
# p is out of bound, but s is not
if i < len(s) and j >= len(p):
return False
# When i is in bound, [s='a', (p='a' or p='.')]
match = i < len(s) and (s[i] == p[j] or p[j] == '.')
# Case when p[j] == '*' and we use it or not
if (j + 1) < len(p) and p[j + 1] == '*':
# If there is a match we can proceed with dfs(i+1, j), [s='a', p='a*']
dP[(i, j)] = (match and dfs(i+1, j)) or dfs(i, j+2)
return dP[(i, j)]
# Case when p does not contain '*'
if match:
dP[(i ,j)] = dfs(i+1, j+1)
return dP[(i ,j)]
# Case when none of the above cases happen
dP[(i ,j)] = False
return dP[(i ,j)]
return dfs(0, 0)Time Complexity: s.
Space Complexity: n of i and n of j.
