10. Regular Expression Matching
Intuition
The problem requires us to implement a regular expression matcher that supports '.' and '*' patterns. The key insight is that we can use dynamic programming to solve this problem efficiently. We need to consider all possible matching scenarios, especially when dealing with the '*' pattern which can match zero or more of the preceding element.
Approach
- Create a 2D DP array where
dp[i][j]
represents whether the substrings[i:]
matches the patternp[j:]
- Initialize the base case:
dp[n][m]
= true (empty string matches empty pattern) - Process the string and pattern from right to left
- For each position, consider two cases:
- If the next character is '*', we can either:
- Skip the current pattern character and '*' (
dp[i][j+2]
) - Match the current character if it matches and continue with the same pattern (
dp[i+1][j]
)
- Skip the current pattern character and '*' (
- If the next character is not '*', simply check if current characters match and move forward
- If the next character is '*', we can either:
Complexity
- Time complexity: O(m*n)
- Space complexity: O(m*n)
Keywords
- Dynamic Programming
- String Matching
- Regular Expression
- Pattern Matching
Code
func isMatch(s string, p string) bool {
n, m := len(s), len(p)
dp := make([][]bool, n + 1)
for i := range dp {
dp[i] = make([]bool, m + 1)
}
dp[n][m] = true
for i := n; i >= 0; i -= 1 {
for j := m - 1; j >= 0; j -= 1 {
currentMatch := i < n && (s[i] == p[j] || p[j] == '.')
if j + 1 < m && p[j + 1] == '*' {
dp[i][j] = dp[i][j + 2] || (currentMatch && dp[i + 1][j])
} else {
dp[i][j] = currentMatch && dp[i + 1][j + 1]
}
}
}
return dp[0][0]
}