Given an input string (s) and a pattern §, implement regular expression matching with support for ‘.’ and ‘’.
‘.’ Matches any single character.
'’ Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
Note
s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
Example
Consideration
- Firstly, we check if p[0] == ‘.’ or s[0] == p[0]
- Secondly, we check if p[1] == ‘*’. If yes, then there are two options-- either isMatch(s, p[2:]) or (the first character matches and isMatch(s[1:], p[0:])). Otherwise, we validate if the first character matches and isMatch(s[1:], p[1:]).
Solution 1: recursive
Time Complexity: space complexity:
class Solution {
public boolean isMatch(String s, String p) {
if(p.isEmpty())
return (s.isEmpty());
boolean firstMatch = (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
if(p.length() >= 2 && p.charAt(1) == '*') {
return firstMatch && isMatch(s.substring(1), p) || isMatch(s, p.substring(2));
} else
return firstMatch && isMatch(s.substring(1), p.substring(1));
}
}
==Solution 2: dynamic programming with top-down ==
Consideration
- we use a 2d array dp[i][j] to store the previous comparison result where i is the index of string s and j is the index of patten p.
- if dp[i][j] is not empty, we just need to return its value.
- if j = p.length(), we check if i has reached the end of the string
- Otherwise, we start from index 0. There are several cases:
(a) If p.charAt(j+1) == ‘’, then dp[i][j] = dp[i][j+2] || (p.charAt(j) == s.charAt(i) || p.charAt(j) == ‘.’ && dp[i+1][j]).
(b) if p.charAt(j+1) != '’, then dp[i][j] = dp[i+1][j+1].
enum Result {
TRUE, FALSE
}
class Solution {
Result[][] memo;
public boolean isMatch(String s, String p) {
memo = new Result[s.length()+1][p.length()+1];
return dp(0,0,s,p);
}
private boolean dp(int i, int j, String s, String p) {
if(memo[i][j] != null) {
return memo[i][j]==Result.TRUE;
} else {
boolean ans;
if(j == p.length()) {
ans = i == s.length();
} else {
boolean first_match = (i < s.length() && (p.charAt(j) == s.charAt(i) || p.charAt(j) == '.'));
if(j+1 < p.length() && p.charAt(j+1) == '*')
ans = dp(i,j+2, s, p) || first_match&&dp(i+1,j,s,p);
else
ans = first_match&&dp(i+1,j+1,s,p);
}
memo[i][j] = ans?Result.TRUE:Result.FALSE;
return ans;
}
}
}
==Solution 2: dynamic programming with bottom-up ==
class Solution {
public boolean isMatch(String s, String p) {
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[s.length()][p.length()] = true;
for(int i = s.length(); i >= 0; i--){
for(int j = p.length()-1; j >= 0; j--) {
boolean first_match = i < s.length() && (p.charAt(j) == s.charAt(i) || p.charAt(j)=='.');
if(j+1 < p.length() && p.charAt(j+1) == '*') {
dp[i][j] = dp[i][j+2] || first_match && dp[i+1][j];
} else
dp[i][j] = first_match && dp[i+1][j+1];
}
}
return dp[0][0];
}
}
Solution 4: dynamic programming
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length(), n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
dp[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (j > 1 && p.charAt(j - 1) == '*') {
dp[i][j] = dp[i][j - 2] || (i > 0 && (s.charAt(i-1) == p.charAt(j - 2) || p.charAt(j-2) == '.') && dp[i - 1][j]);
} else {
dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s.charAt(i-1) == p.charAt(j - 1) || p.charAt(j - 1) == '.');
}
}
}
return dp[m][n];
}
}
Reference
https://leetcode.com/problems/regular-expression-matching/solution/