You are given an input string s consisting of lowercase english letters, and a pattern p consisting of lowercase english letters, as well as '.', and '*' characters.
Return true if the pattern matches the entire input string, otherwise return false.
'.' Matches any single character'*' Matches zero or more of the preceding element.Example 1:
Input: s = "aa", p = ".b"
Output: falseExplanation: Regardless of which character we choose for the '.' in the pattern, we cannot match the second character in the input string.
Example 2:
Input: s = "nnn", p = "n*"
Output: trueExplanation: '*' means zero or more of the preceding element, 'n'. We choose 'n' to repeat three times.
Example 3:
Input: s = "xyz", p = ".*z"
Output: trueExplanation: The pattern ".*" means zero or more of any character, so we choose ".." to match "xy" and "z" to match "z".
Constraints:
1 <= s.length <= 201 <= p.length <= 20'*', will be preceded by a valid character or '.'.
You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s and n is the length of the string p.
Try to think in terms of recursion and visualize it as a decision tree, where we explore different combinations to match the strings when encountering *. Multiple decisions are made at each step to find a valid matching path. Can you determine the possible decisions at each recursion step?
We recursively iterate through the strings using indices i and j for s and p, respectively. If the characters match or p[j] is '.', we increment both i and j to process the remaining strings. When the next character of string p is '*', we have two choices: skip it (treating it as zero occurrences) or match one or more characters (if s[i] matches p[j]), incrementing i accordingly.
If both indices go out of bounds, we return true; otherwise, we return false. If any recursive path returns true, we immediately return true. This approach is exponential. Can you think of a way to optimize it?
We can use memoization to cache the results of recursive calls and avoid redundant calculations. A hash map or a 2D array can be used to store these results.
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
def dfs(i, j):
if j == n:
return i == m
match = i < m and (s[i] == p[j] or p[j] == ".")
if (j + 1) < n and p[j + 1] == "*":
return (dfs(i, j + 2) or # don't use *
(match and dfs(i + 1, j))) # use *
if match:
return dfs(i + 1, j + 1)
return False
return dfs(0, 0)Where is the length of the string and is the length of the string .
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
cache = {}
def dfs(i, j):
if j == n:
return i == m
if (i, j) in cache:
return cache[(i, j)]
match = i < m and (s[i] == p[j] or p[j] == ".")
if (j + 1) < n and p[j + 1] == "*":
cache[(i, j)] = (dfs(i, j + 2) or
(match and dfs(i + 1, j)))
return cache[(i, j)]
if match:
cache[(i, j)] = dfs(i + 1, j + 1)
return cache[(i, j)]
cache[(i, j)] = False
return False
return dfs(0, 0)Where is the length of the string and is the length of the string .
class Solution:
def isMatch(self, s: str, p: str) -> bool:
dp = [[False] * (len(p) + 1) for i in range(len(s) + 1)]
dp[len(s)][len(p)] = True
for i in range(len(s), -1, -1):
for j in range(len(p) - 1, -1, -1):
match = i < len(s) and (s[i] == p[j] or p[j] == ".")
if (j + 1) < len(p) and p[j + 1] == "*":
dp[i][j] = dp[i][j + 2]
if match:
dp[i][j] = dp[i + 1][j] or dp[i][j]
elif match:
dp[i][j] = dp[i + 1][j + 1]
return dp[0][0]Where is the length of the string and is the length of the string .
class Solution:
def isMatch(self, s: str, p: str) -> bool:
dp = [False] * (len(p) + 1)
dp[len(p)] = True
for i in range(len(s), -1, -1):
nextDp = [False] * (len(p) + 1)
nextDp[len(p)] = (i == len(s))
for j in range(len(p) - 1, -1, -1):
match = i < len(s) and (s[i] == p[j] or p[j] == ".")
if (j + 1) < len(p) and p[j + 1] == "*":
nextDp[j] = nextDp[j + 2]
if match:
nextDp[j] |= dp[j]
elif match:
nextDp[j] = dp[j + 1]
dp = nextDp
return dp[0]Where is the length of the string and is the length of the string .
class Solution:
def isMatch(self, s: str, p: str) -> bool:
dp = [False] * (len(p) + 1)
dp[len(p)] = True
for i in range(len(s), -1, -1):
dp1 = dp[len(p)]
dp[len(p)] = (i == len(s))
for j in range(len(p) - 1, -1, -1):
match = i < len(s) and (s[i] == p[j] or p[j] == ".")
res = False
if (j + 1) < len(p) and p[j + 1] == "*":
res = dp[j + 2]
if match:
res |= dp[j]
elif match:
res = dp1
dp[j], dp1 = res, dp[j]
return dp[0]Where is the length of the string and is the length of the string .