Problem Statement:
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).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") ? false isMatch("aa","aa") ? true isMatch("aaa","aa") ? false
isMatch("aa", "a*") ? true
isMatch("aa", ".*") ? true
isMatch("ab", ".*") ? true
isMatch("aab", "c*a*b") ? true
I have two Java implementations; one uses recursion (complexity \$O(2^{m+n})\$) , the other dynamic programming (complexity \$O(mn)\$).
I am looking for feedbacks on how to write better, more elegant code. Any suggestions on style, simplicity, comments, etc., are welcome.
Assumptions I made:
- s is a valid string not including '.'and '*'
- p is a legal regular expression
Recursive version:
public class Solution {
static public boolean charMatch(char c, char p){
return c == p || p == '.';
}
static public boolean nextStar(String p, int pIdx){
if(pIdx + 1 >= p.length()){
return false;
}
return p.charAt(pIdx+1) == '*';
}
static public boolean partialMatch(String s, int sIdx, String p, int pIdx){
//an empty pattern only matches empty string
if(pIdx == p.length()){
return sIdx == s.length();
}
//pattern starts with 'x*', check if we can simply skip it and still match
if(nextStar(p, pIdx)) {
if (partialMatch(s, sIdx, p, pIdx + 2)) return true;
};
//empty string, but cannot match the pattern by skipping the 'x*' pattern, so match fails
if(sIdx == s.length()) return false;
//match first character and see if the rest matches
if(nextStar(p, pIdx)) {
return charMatch(s.charAt(sIdx), p.charAt(pIdx)) && partialMatch(s, sIdx + 1, p, pIdx);
}
return charMatch(s.charAt(sIdx), p.charAt(pIdx)) && partialMatch(s, sIdx + 1, p, pIdx+1);
}
static public boolean isMatch(String s, String p) {
return partialMatch(s, 0, p, 0);
}
}
DP version:
public class RegularExpressionSolution5 {
static private boolean charMatch(char c, char p){
return c == p || p == '.';
}
static private boolean nextStar(String p, int pIdx){
if(pIdx + 1 >= p.length()){
return false;
}
return p.charAt(pIdx+1) == '*';
}
// matchMatrix[x][y] --> if s.substr(x, s.length) matches p.substr(y, p.length)
// note that p.substr(y, p.length) may not be a valid regular expression (starts with '*'),
// in which case the matrix is filled with false
static public void fillMatchMatrix(String s, String p, boolean[][] matchMatrix){
for(int sIdx = s.length(); sIdx >= 0; sIdx--){
for(int pIdx = p.length(); pIdx >= 0; pIdx--){
//an empty pattern only matches empty string
if(pIdx == p.length()){
matchMatrix[sIdx][pIdx] = (sIdx == s.length());
continue;
}
//pattern starts with 'x*', check if we can simply skip it and still match
if(nextStar(p, pIdx) && matchMatrix[sIdx][pIdx+2] == true){
matchMatrix[sIdx][pIdx] = true;
continue;
}
//empty string, but cannot match the pattern by skipping the 'x*' pattern, so match fails
if(sIdx == s.length()) {
matchMatrix[sIdx][pIdx] = false;
continue;
}
//match first character and see if next matches
if(nextStar(p, pIdx)){
matchMatrix[sIdx][pIdx] = charMatch(s.charAt(sIdx), p.charAt(pIdx)) && matchMatrix[sIdx+1][pIdx];
}else{
matchMatrix[sIdx][pIdx] = charMatch(s.charAt(sIdx), p.charAt(pIdx)) && matchMatrix[sIdx+1][pIdx+1];
}
}
}
}
static public boolean isMatch(String s, String p) {
boolean[][] matchMatrix = new boolean[s.length()+1][p.length()+1];
fillMatchMatrix(s, p, matchMatrix);
return matchMatrix[0][0];
}
}